commit
10af3ba114
|
@ -0,0 +1,99 @@
|
|||
## Local Azure Function and SPFx Web Part Development to consume third party APIs
|
||||
|
||||
This sample shows how to consume third-party APIs through an Azure Functions by a Web Part. In this scenario, Vimeo is the representative third party API.
|
||||
This project contains two separate project folders:
|
||||
|
||||
* [VimeoRequest](./VimeoRequest) - contain the Azure Function written in JavaScript
|
||||
* [VimeoWebPart](./VimeoWebPart) - contain the web part consuming the local running Azure Function
|
||||
|
||||
## Used SharePoint Framework Version
|
||||
![drop](https://img.shields.io/badge/drop-1.4.1-green.svg)
|
||||
|
||||
## Applies to
|
||||
|
||||
* [SharePoint Framework Developer](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-azfunc-vimeo | Stefan Bauer - n8d ([@stfbauer](https://twitter.com/stfbauer))
|
||||
|
||||
## Version history
|
||||
|
||||
Version|Date|Comments
|
||||
-------|----|--------
|
||||
1.0|July 24, 2018|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.**
|
||||
|
||||
## Build Azure Function
|
||||
|
||||
To install and run the Azure Function navigate to the folder: [VimeoRequest](./VimeoRequest) and execute the following command.
|
||||
|
||||
```sh
|
||||
npm install
|
||||
```
|
||||
|
||||
This will install all the required NPM packages to run the Azure function
|
||||
|
||||
### Additional Configuration Vimeo Azure Function
|
||||
|
||||
The folder [VimeoRequest](./VimeoRequest) contains a Azure Function generated with the [Azure Function Core Tools](https://docs.microsoft.com/en-us/azure/azure-functions/functions-run-local).
|
||||
The access videos stored on this platform first a new application needs to be created first. To create a new Application navigate to the following website [https://developer.vimeo.com/apps/new?source=topnav](https://developer.vimeo.com/apps/new?source=topnav)
|
||||
After the creation of a new Application, the created AppID and Secret must get stored in the local Azure function configuration. This configuration file can be found in the root folder of the Azure Function Host and is named [local.settings.json](./VimeoRequest/local.settings.json).
|
||||
|
||||
```jS
|
||||
{
|
||||
"IsEncrypted": false,
|
||||
"Values": {
|
||||
"AzureWebJobsStorage": "",
|
||||
"VimeoAPI": "<Enter APP ID here>",
|
||||
"VimeoSecret": "<Enter App Secret here>",
|
||||
"VimeoEndPoint": "https://api.vimeo.com"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
A detailed step by step guide on this Azure Function can be found on the blog post: [An Azure Function for a Web Part](https://n8d.at/blog/an-azure-function-for-an-smart-stupid-web-part-part-2/).
|
||||
|
||||
#### Run Azure Function
|
||||
|
||||
To run this Azure Function execute the following command from inside the VimeoRequest folder.
|
||||
|
||||
```sh
|
||||
func start --useHttps --cert server.pfx --password 'password' --cors '*'
|
||||
```
|
||||
|
||||
To check if the Azure Function is running and returns a result navigate to the following URL in your browser.
|
||||
|
||||
```
|
||||
https://localhost:7071/api/Search?q=Hello%20World
|
||||
```
|
||||
|
||||
#### Run Vimeo Web Part
|
||||
|
||||
To run the web part execute the following command:
|
||||
|
||||
```bash
|
||||
git clone the repo
|
||||
npm i
|
||||
npm i -g gulp
|
||||
gulp serve
|
||||
```
|
||||
|
||||
This package produces the following:
|
||||
|
||||
* lib/* - intermediate-stage commonjs build artifacts
|
||||
* dist/* - the bundled script, along with other resources
|
||||
* deploy/* - all resources which should be uploaded to a CDN.
|
||||
|
||||
## Further information
|
||||
- Overall Overview: [A smart stupid web part for SharePoint - Part 1](https://n8d.at/blog/smart-stupid-web-parts-with-sharepoint-framework-part-1/)
|
||||
- Details to Azure function implementation: [An Azure Function for an smart stupid web part – Part 2](https://n8d.at/blog/an-azure-function-for-an-smart-stupid-web-part-part-2/)
|
||||
- Web Part Details: [A smart stupid web part consumes a third party API through Azure Functions – Part 3](https://n8d.at/blog/a-smart-stupid-web-part-consumes-a-third-party-api-through-azure-functions-part-3/)
|
||||
|
||||
<img src="https://telemetry.sharepointpnp.com/sp-dev-fx-webparts/samples/react-azfunc-vimeo" />
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
|
||||
bin
|
||||
obj
|
||||
csx
|
||||
.vs
|
||||
edge
|
||||
Publish
|
||||
|
||||
*.user
|
||||
*.suo
|
||||
*.cscfg
|
||||
*.Cache
|
||||
project.lock.json
|
||||
|
||||
/packages
|
||||
/TestResults
|
||||
|
||||
/tools/NuGet.exe
|
||||
/App_Data
|
||||
/secrets
|
||||
/data
|
||||
.secrets
|
||||
appsettings.json
|
||||
local.settings.json
|
||||
|
||||
node_modules
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"disabled": false,
|
||||
"bindings": [
|
||||
{
|
||||
"authLevel": "function",
|
||||
"type": "httpTrigger",
|
||||
"direction": "in",
|
||||
"name": "req"
|
||||
},
|
||||
{
|
||||
"type": "http",
|
||||
"direction": "out",
|
||||
"name": "res"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,143 @@
|
|||
'use strict';
|
||||
var Vimeo = require('vimeo').Vimeo;
|
||||
|
||||
const VimeoAPIKey = process.env.VimeoAPI,
|
||||
VimeoAPISecret = process.env.VimeoSecret,
|
||||
VimeoEndPoint = process.env.VimeoEndPoint;
|
||||
|
||||
var _handleError = {
|
||||
noQuery: (context) => {
|
||||
|
||||
// return error context
|
||||
context.res = {
|
||||
status: 400,
|
||||
body: "No search query has been passed in please specifiy ?q=Hello World"
|
||||
};
|
||||
context.done();
|
||||
|
||||
// throw exception
|
||||
throw "No query was specified please use ?q=VideoToFind";
|
||||
|
||||
},
|
||||
APIError: (context, message) => {
|
||||
|
||||
// return error context
|
||||
context.res = {
|
||||
status: 400,
|
||||
body: "API Error: " + message
|
||||
};
|
||||
context.done();
|
||||
|
||||
// throw exception
|
||||
throw "API Error" + message;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
async function queryVimeo(search, client) {
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
client.request({
|
||||
// This returns the first page of videos containing the term "vimeo staff".
|
||||
// These videos will be sorted by most relevant to least relevant.
|
||||
path: '/videos',
|
||||
query: {
|
||||
page: 1,
|
||||
per_page: 10,
|
||||
query: search,
|
||||
sort: 'relevant',
|
||||
direction: 'asc'
|
||||
}
|
||||
|
||||
}, (error, body, statusCode, headers) => {
|
||||
|
||||
console.log(error);
|
||||
|
||||
if (error) {
|
||||
|
||||
reject();
|
||||
throw error;
|
||||
|
||||
}
|
||||
|
||||
resolve({
|
||||
header: headers,
|
||||
body: body
|
||||
});
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
};
|
||||
|
||||
module.exports = (context, req) => {
|
||||
|
||||
if (Object.keys(req.query).length === 0) {
|
||||
throw _handleError.noQuery(context);
|
||||
}
|
||||
|
||||
if (req.query.q === undefined) {
|
||||
|
||||
throw _handleError.noQuery(context);
|
||||
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
var client = new Vimeo(VimeoAPIKey, VimeoAPISecret);
|
||||
|
||||
client.generateClientCredentials(["public"], (err, response) => {
|
||||
|
||||
console.log(response);
|
||||
|
||||
if (err) {
|
||||
|
||||
throw err;
|
||||
context.done();
|
||||
|
||||
}
|
||||
|
||||
client.setAccessToken(response.access_token);
|
||||
|
||||
try {
|
||||
|
||||
queryVimeo(req.query.q, client)
|
||||
.then((searchResponse) => {
|
||||
|
||||
console.log(searchResponse.header);
|
||||
|
||||
context.res = {
|
||||
body: searchResponse.body
|
||||
};
|
||||
|
||||
context.done();
|
||||
|
||||
}).catch(
|
||||
(error) => {
|
||||
|
||||
throw error;
|
||||
context.done();
|
||||
|
||||
}
|
||||
|
||||
)
|
||||
|
||||
} catch (err) {
|
||||
|
||||
context.log('error', err);
|
||||
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
} catch (error) {
|
||||
|
||||
context.res = {
|
||||
body: error
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
};
|
|
@ -0,0 +1,47 @@
|
|||
{
|
||||
"name": "vimeo-search-api",
|
||||
"version": "0.0.1",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
"buffer-from": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-0.1.2.tgz",
|
||||
"integrity": "sha512-RiWIenusJsmI2KcvqQABB83tLxCByE3upSP8QU3rJDMVFGPWLvPQJt/O1Su9moRWeH7d+Q2HYb68f6+v+tw2vg=="
|
||||
},
|
||||
"extend": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz",
|
||||
"integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ="
|
||||
},
|
||||
"lodash.throttle": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz",
|
||||
"integrity": "sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ="
|
||||
},
|
||||
"resolve-url": {
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
|
||||
"integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo="
|
||||
},
|
||||
"tus-js-client": {
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/tus-js-client/-/tus-js-client-1.5.1.tgz",
|
||||
"integrity": "sha512-qBSNXpc6ZPe6stn4NSkQ1dnVhVblPAtQo6037g5Qr5zr9gGX1gr+8e0+HtQMBp22Ouo6LYesWMdKbcOR5sRj5A==",
|
||||
"requires": {
|
||||
"buffer-from": "^0.1.1",
|
||||
"extend": "^3.0.0",
|
||||
"lodash.throttle": "^4.1.1",
|
||||
"resolve-url": "^0.2.1"
|
||||
}
|
||||
},
|
||||
"vimeo": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/vimeo/-/vimeo-2.0.2.tgz",
|
||||
"integrity": "sha512-BAxfH2B787TcCHDN3MipluLPV92NK0yF5PYIXpXnvt1w7yy/UsI9Cxo5boL50QQjC038ULLNeneGGTskES6SmA==",
|
||||
"requires": {
|
||||
"tus-js-client": "^1.4.5"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"name": "vimeo-search-api",
|
||||
"version": "0.0.1",
|
||||
"description": "Vimeo Search Api for use in Web Parts",
|
||||
"private": true,
|
||||
"main": "index.js",
|
||||
"dependencies": {
|
||||
"vimeo": "^2.0.2"
|
||||
},
|
||||
"devDependencies": {},
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "Stefan Bauer",
|
||||
"license": "ISC"
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"name": "Azure"
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
#!/bin/bash
|
||||
func start --useHttps --cert server.pfx --password 'password' --cors '*'
|
|
@ -0,0 +1 @@
|
|||
{ }
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"IsEncrypted": false,
|
||||
"Values": {
|
||||
"AzureWebJobsStorage": "",
|
||||
"VimeoAPI": "<Enter your APPID here>",
|
||||
"VimeoSecret": "<Enter your Secret here>",
|
||||
"VimeoEndPoint": "https://api.vimeo.com"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIDQDCCAigCCQCmyduWRZNakzANBgkqhkiG9w0BAQsFADBiMQswCQYDVQQGEwJB
|
||||
VDEPMA0GA1UECAwGVmllbm5hMQ8wDQYDVQQHDAZWaWVubmExDDAKBgNVBAoMA044
|
||||
RDEPMA0GA1UECwwGRGVzaWduMRIwEAYDVQQDDAlsb2NhbGhvc3QwHhcNMTgwNTE2
|
||||
MTEyOTExWhcNMjAwNTE1MTEyOTExWjBiMQswCQYDVQQGEwJBVDEPMA0GA1UECAwG
|
||||
Vmllbm5hMQ8wDQYDVQQHDAZWaWVubmExDDAKBgNVBAoMA044RDEPMA0GA1UECwwG
|
||||
RGVzaWduMRIwEAYDVQQDDAlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IB
|
||||
DwAwggEKAoIBAQC/NnBOEbCJPEmWtLj2zibQsmfutHoinTivGxNhG8C+DvNU0NZD
|
||||
wRKztmHdal/Z3oSgGYiKdADkWR/rk4ZemIpezRq/IdL3N6kJ0B73X8tGjfUqnmGE
|
||||
9ocoYKuRd8QwSyaV3GhFhIIJajBno9V2GILfTpywa7YTSVrqY11qK1q6lluC5zLK
|
||||
SreYhRO1fj74Y2bYz+s0I7GEomfse0Y3/Ju+oxWWItgWzmkUPhDCLAbvq3x3dt2P
|
||||
aWrXGGh62vrlosq9uz7IVLVaN8nkhjLjiZagsD2T+MXfwkPeNfXAzOQiMSKZeslU
|
||||
ItmzfmbNMIh++En705EMZXakpudLGapHq1ZZAgMBAAEwDQYJKoZIhvcNAQELBQAD
|
||||
ggEBAAt3c5i26yuBAogGpJn3pQfKulBSRkWtosm8rlHwdiWcUG/Gqi82VNuc7Yyf
|
||||
gcNBHT+91kutP5lWWnm2waCGqRR9gyeBv3oKLYzay3CP/Pp2gsPUOn5kg5Tf4G6T
|
||||
hFQgF+S6wR38Tgrw2IedwiXO2ayW4Rar6pBy9hVUfBOo3TxZWS05EJWKsgVo0ixN
|
||||
GbJDFlbGToieX0tsOTjfOH+mZI6tGxtFAQ6HwmzEvxMoJeG6cENdaxasIpLbtirN
|
||||
k8FAI9GVZVl89/lPgnTZXp6MN1k6EwbjmJWN4LYHllKuYOoszuJcrhYPWgZ4WIUM
|
||||
QPtXr505dSOBUn31a/PR0YlJdoE=
|
||||
-----END CERTIFICATE-----
|
|
@ -0,0 +1,17 @@
|
|||
-----BEGIN CERTIFICATE REQUEST-----
|
||||
MIICpzCCAY8CAQAwYjELMAkGA1UEBhMCQVQxDzANBgNVBAgMBlZpZW5uYTEPMA0G
|
||||
A1UEBwwGVmllbm5hMQwwCgYDVQQKDANOOEQxDzANBgNVBAsMBkRlc2lnbjESMBAG
|
||||
A1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
|
||||
vzZwThGwiTxJlrS49s4m0LJn7rR6Ip04rxsTYRvAvg7zVNDWQ8ESs7Zh3Wpf2d6E
|
||||
oBmIinQA5Fkf65OGXpiKXs0avyHS9zepCdAe91/LRo31Kp5hhPaHKGCrkXfEMEsm
|
||||
ldxoRYSCCWowZ6PVdhiC306csGu2E0la6mNdaitaupZbgucyykq3mIUTtX4++GNm
|
||||
2M/rNCOxhKJn7HtGN/ybvqMVliLYFs5pFD4QwiwG76t8d3bdj2lq1xhoetr65aLK
|
||||
vbs+yFS1WjfJ5IYy44mWoLA9k/jF38JD3jX1wMzkIjEimXrJVCLZs35mzTCIfvhJ
|
||||
+9ORDGV2pKbnSxmqR6tWWQIDAQABoAAwDQYJKoZIhvcNAQELBQADggEBAJiDgXVA
|
||||
dWGQ23MheP0LL0BSDV9YvPDQIv2enO0dQlTACdQ7d36kmKEvciEQPbIN+auIdDzU
|
||||
6DzGDr4DeBWujMta3VaEHzVLL97isPAvQeZlAGqIcxGj8FXxZLdjb0lhKdCHobLP
|
||||
QyrVRvdCYpzRk31fNMN2ce9w53lonNY0DHAM52V/QblAJtsSjCfJiszlFumKzGJJ
|
||||
Z2rc6JZVr3yGLzJ8GsMmiNZI/i0ZXYbm+/0dyQP5duxagxbvN0kNssQUcRB5HBPW
|
||||
/w0pmnj9CXDnMc6LFqrawV1OUbTq8zZxKr/Z5J01TDXdE0mu2C4/VbhlYFtauzwI
|
||||
hnIRBCFiLyoK3Vo=
|
||||
-----END CERTIFICATE REQUEST-----
|
|
@ -0,0 +1,30 @@
|
|||
-----BEGIN RSA PRIVATE KEY-----
|
||||
Proc-Type: 4,ENCRYPTED
|
||||
DEK-Info: DES-EDE3-CBC,8AEF0DAC87FA102A
|
||||
|
||||
j+ubxX2TjHxuJpSpEwfUdjMZcnq2WT8cCTnoCB2fj7JlKpbGWOenFBhbSAlg+3eY
|
||||
bjkoVlZxV4w1M73c5mLy+AIxUmrPE27y/2axhviYsxCLhtmHBSPflJUou5emtAmk
|
||||
ExpuIkk2SvPPiwUAICvYxW1Tt1eGLSMme98vczBm4berap5AbNf+jtTFsU9kzRig
|
||||
ItfbadPepPKSp9ERh5lChbuVADshwpppy5kvcGkD5Vg4US8CMSWU4f9cWFbIU+Md
|
||||
l8Rfk8iYRRynSm4y1dlKhd4rh0JwDH9nlTAQEOpBGQUdyhS0wcYwO1rRo8wQefAe
|
||||
K+/im/f9Gp+AhopeRj/+FRZGN9Vwg/+s1Is0QbEPSztUPFeI8i5zBjjsIkEjvnlN
|
||||
Ces7QmutEUtx+hLvskvDChuP1HTsI7ji9m/aLHq4cuswm+FPuwUDGiauyTpczfeN
|
||||
dEWdxTravBcrhzOqRTgAXXx+FXhdSgn9ZPYbBJuX3O0kJ+xcqEWuz0FE4d1lB4bR
|
||||
5qI81+fsOVxa4wU9n7PSLG2pHVu8nC3P+cRQCofmz77LRl4KmXNpIFOKUgvI8GMe
|
||||
UDkmOaIYIefYrlWzds7OQxRtIK22ktX3zzFeEMjutkcyS7pRogPJ2uwlmJ8TB9dH
|
||||
BztjobA5LvZ32KcfwFJoV2HS1bVqJYvsO1+8mUnO/9Or4KEi8TLMTdwfGkgNjByZ
|
||||
ioR8PktNZjPsBAeslP7z4IVPBngsTKV1xqen2p/twJcozuW1dA+fOFFVaihb6ERn
|
||||
hicx+b9MWnZP9zMfn3YhGEeCL17WIIrO/UDPaApVgKmQmxjujZ9opuABWgteJ2mR
|
||||
/M5cMig/uqgv6vNfCjEBmesckD2XGQpSgVSwalxQkKMRpgYsl0t5eAGiQne2FswF
|
||||
2u6WomgN18eDgQoqSV5KBdyDhMIuHjEHnLJ8+oCq6nQ6QH27x1N7I252PhQ70fts
|
||||
NS6AoupTuToQEeQXxruLa2BZhOLZyCnqAaSO7pBMrR861F68R/Vw5KLHau+CpiVF
|
||||
Mr86eloiHjl/lVeuBcwmXvqTTsonuolHIC/4Qi6FuEu5GNWhlkhoT1wwf+bcAfir
|
||||
shGQCAgLFVNJJbSiuWnmnNLpgJ1rJi2Nv5FM2gMrZ/VVYXgsVq/b14lJHc6lZQF7
|
||||
8Y8JQGDYWVYdnU4g2Puc+S9OZgb1vJpp9rdbX1D8vrHe8NW0O2p9Esk3Yum/fbaD
|
||||
pQXsiovFXhfstUCbBiMtBRYzpim7wLutpCeAfWNtdF1CGXHjNnT18vwaOm0T3kB3
|
||||
pc1xnm0R9kgT9Xo+kBboQ8YZtpstQpagSy2UraBVY0he340vrsjusyxVWhepi8GF
|
||||
9yitPwd2hwi7BlGC0rvLetFHbLwgZTMzUvwEwL6UFzBryo1Oa3WVmL4azQzgBr9g
|
||||
UkEbzuNrYE1EXamz10+uWJp0ht81j8OMJRWQ49AB9Qlx+OPH4GjYAbTtXu72k/h0
|
||||
v9CnABfLUfVnuEvNb2aLIPIfytGxp6YJKJDX2c8NAdFh5rAc3+7h+eTpfYR1N3Bi
|
||||
VvzVRUSnC0QtS6yYDk3lp+4etIjf4XIkoAL9rhVkHw86r0ZWNoAPShP/D8WDJjD4
|
||||
-----END RSA PRIVATE KEY-----
|
Binary file not shown.
|
@ -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
|
|
@ -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
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"@microsoft/generator-sharepoint": {
|
||||
"version": "1.4.1",
|
||||
"libraryName": "vimeo-web-part",
|
||||
"libraryId": "46352013-f007-40a8-80a1-5e4247dbe22d",
|
||||
"environment": "spo"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"$schema": "https://dev.office.com/json-schemas/spfx-build/config.2.0.schema.json",
|
||||
"version": "2.0",
|
||||
"bundles": {
|
||||
"vimeo-web-part": {
|
||||
"components": [
|
||||
{
|
||||
"entrypoint": "./lib/webparts/vimeo/VimeoWebPart.js",
|
||||
"manifest": "./src/webparts/vimeo/VimeoWebPart.manifest.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"externals": {},
|
||||
"localizedResources": {
|
||||
"VimeoWebPartStrings": "lib/webparts/vimeo/loc/{locale}.js"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"$schema": "https://dev.office.com/json-schemas/spfx-build/copy-assets.schema.json",
|
||||
"deployCdnPath": "temp/deploy"
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"$schema": "https://dev.office.com/json-schemas/spfx-build/deploy-azure-storage.schema.json",
|
||||
"workingDir": "./temp/deploy/",
|
||||
"account": "<!-- STORAGE ACCOUNT NAME -->",
|
||||
"container": "vimeo-web-part",
|
||||
"accessKey": "<!-- ACCESS KEY -->"
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"$schema": "https://dev.office.com/json-schemas/spfx-build/package-solution.schema.json",
|
||||
"solution": {
|
||||
"name": "vimeo-web-part-client-side-solution",
|
||||
"id": "46352013-f007-40a8-80a1-5e4247dbe22d",
|
||||
"version": "1.0.0.0",
|
||||
"includeClientSideAssets": true
|
||||
},
|
||||
"paths": {
|
||||
"zippedPackage": "solution/vimeo-web-part.sppkg"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"$schema": "https://dev.office.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/"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
{
|
||||
"$schema": "https://dev.office.com/json-schemas/core-build/tslint.schema.json",
|
||||
// Display errors as warnings
|
||||
"displayAsWarning": true,
|
||||
// The TSLint task may have been configured with several custom lint rules
|
||||
// before this config file is read (for example lint rules from the tslint-microsoft-contrib
|
||||
// project). If true, this flag will deactivate any of these rules.
|
||||
"removeExistingRules": true,
|
||||
// When true, the TSLint task is configured with some default TSLint "rules.":
|
||||
"useDefaultConfigAsBase": false,
|
||||
// Since removeExistingRules=true and useDefaultConfigAsBase=false, there will be no lint rules
|
||||
// which are active, other than the list of rules below.
|
||||
"lintConfig": {
|
||||
// Opt-in to Lint rules which help to eliminate bugs in JavaScript
|
||||
"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-case": true,
|
||||
"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,
|
||||
"valid-typeof": true,
|
||||
"variable-name": false,
|
||||
"whitespace": false
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"$schema": "https://dev.office.com/json-schemas/spfx-build/write-manifests.schema.json",
|
||||
"cdnBasePath": "<!-- PATH TO CDN -->"
|
||||
}
|
|
@ -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);
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,33 @@
|
|||
{
|
||||
"name": "vimeo-web-part",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "gulp bundle",
|
||||
"clean": "gulp clean",
|
||||
"test": "gulp test"
|
||||
},
|
||||
"dependencies": {
|
||||
"react": "15.6.2",
|
||||
"react-dom": "15.6.2",
|
||||
"@types/react": "15.6.6",
|
||||
"@types/react-dom": "15.5.6",
|
||||
"@microsoft/sp-core-library": "~1.4.1",
|
||||
"@microsoft/sp-webpart-base": "~1.4.1",
|
||||
"@microsoft/sp-lodash-subset": "~1.4.1",
|
||||
"@microsoft/sp-office-ui-fabric-core": "~1.4.1",
|
||||
"@types/webpack-env": ">=1.12.1 <1.14.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@microsoft/sp-build-web": "~1.4.1",
|
||||
"@microsoft/sp-module-interfaces": "~1.4.1",
|
||||
"@microsoft/sp-webpart-workbench": "~1.4.1",
|
||||
"gulp": "~3.9.1",
|
||||
"@types/chai": ">=3.4.34 <3.6.0",
|
||||
"@types/mocha": ">=2.2.33 <2.6.0",
|
||||
"ajv": "~5.2.2"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"$schema": "https://dev.office.com/json-schemas/spfx/client-side-web-part-manifest.schema.json",
|
||||
"id": "8937220d-c8ec-4411-af5d-1ff527a65953",
|
||||
"alias": "VimeoWebPart",
|
||||
"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": "Vimeo" },
|
||||
"description": { "default": "Vimeo Web part" },
|
||||
"officeFabricIconFontName": "Page",
|
||||
"properties": {
|
||||
"VimeoUrl": "Vimeo"
|
||||
}
|
||||
}]
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
import * as React from 'react';
|
||||
import * as ReactDom from 'react-dom';
|
||||
import { Version } from '@microsoft/sp-core-library';
|
||||
import {
|
||||
BaseClientSideWebPart,
|
||||
IPropertyPaneConfiguration,
|
||||
PropertyPaneTextField,
|
||||
IWebPartEvent,
|
||||
WebPartContext
|
||||
} from '@microsoft/sp-webpart-base';
|
||||
|
||||
import * as strings from 'VimeoWebPartStrings';
|
||||
import Vimeo from './components/Vimeo';
|
||||
import { IVimeoProps } from './components/IVimeoProps';
|
||||
import { HttpClient } from '@microsoft/sp-http';
|
||||
|
||||
|
||||
export interface IVimeoWebPartProps {
|
||||
VimeoUrl: string;
|
||||
httpClient: HttpClient;
|
||||
onSave: any;
|
||||
properties: any;
|
||||
editMode: boolean;
|
||||
}
|
||||
|
||||
export default class VimeoWebPart extends BaseClientSideWebPart<IVimeoWebPartProps> {
|
||||
|
||||
public render(): void {
|
||||
|
||||
const element: React.ReactElement<IVimeoProps> = React.createElement(
|
||||
Vimeo,
|
||||
{
|
||||
VimeoUrl: this.properties.VimeoUrl,
|
||||
httpClient: this.context.httpClient,
|
||||
onSave: this.onSave,
|
||||
properties: this.properties,
|
||||
editMode: this.displayMode === 2 ? true : false
|
||||
}
|
||||
);
|
||||
|
||||
ReactDom.render(element, this.domElement);
|
||||
|
||||
}
|
||||
|
||||
private onSave(item) {
|
||||
|
||||
this.properties.VimeoUrl = item.url;
|
||||
|
||||
}
|
||||
|
||||
protected get dataVersion(): Version {
|
||||
return Version.parse('1.0');
|
||||
}
|
||||
|
||||
protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
|
||||
return {
|
||||
pages: [
|
||||
{
|
||||
header: {
|
||||
description: strings.PropertyPaneDescription
|
||||
},
|
||||
groups: [
|
||||
{
|
||||
groupName: strings.BasicGroupName,
|
||||
groupFields: [
|
||||
PropertyPaneTextField('VimeoUrl', {
|
||||
label: strings.DescriptionFieldLabel
|
||||
})
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
export interface IVimeoPitctures{
|
||||
active: boolean;
|
||||
resource_key: string;
|
||||
sizes: Array<any>;
|
||||
type: string;
|
||||
uri: string;
|
||||
}
|
||||
|
||||
export interface IVimeoUser{
|
||||
account: string;
|
||||
bio: string;
|
||||
created_time: string;
|
||||
link: string;
|
||||
location: string;
|
||||
metadata: any;
|
||||
name: any;
|
||||
pictures: any;
|
||||
resource_key: string;
|
||||
uri: string;
|
||||
websites: any;
|
||||
}
|
||||
|
||||
// export interface IVimeoEntryProps{
|
||||
// title?: string;
|
||||
// description: string;
|
||||
// author?: IVimeoUser;
|
||||
// url?: string;
|
||||
// playerProps?: string;
|
||||
// pictures?: IVimeoPitctures;
|
||||
// showVideo: boolean;
|
||||
// onClick?: any;
|
||||
// }
|
||||
export interface IVimeoEntryProps{
|
||||
item: any;
|
||||
onShowVideo: any;
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
export interface IVimeoProps {
|
||||
VimeoUrl: string;
|
||||
httpClient: any;
|
||||
onSave: Function;
|
||||
editMode: boolean;
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
export interface IVimeoSearchProps {
|
||||
httpClient: any;
|
||||
onSave: Function;
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
.test {
|
||||
color: black
|
||||
}
|
|
@ -0,0 +1,155 @@
|
|||
@import '~@microsoft/sp-office-ui-fabric-core/dist/sass/SPFabricCore.scss';
|
||||
.vimeo {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.videoEntry {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.videoContainer {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.videoInfo {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.videoEntry {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
// .videoPlayer {
|
||||
// border: 0;
|
||||
// border-collapse: collapse;
|
||||
// width: 50%;
|
||||
// height: auto;
|
||||
// }
|
||||
.videoPlayerOuter {
|
||||
position: relative;
|
||||
padding: 56.25% 0 0 0;
|
||||
}
|
||||
|
||||
.videoPlayer {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: black;
|
||||
}
|
||||
|
||||
.videoPreviewPane {
|
||||
border-top: 1px black solid;
|
||||
border-bottom: 1px black solid;
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
|
||||
.videoPlayerToolbar {
|
||||
background-color: white;
|
||||
padding-bottom: 0.5em;
|
||||
padding-top: 0.5em;
|
||||
}
|
||||
|
||||
.videoPlayerHeader {
|
||||
@extend .videoPlayerToolbar;
|
||||
@include ms-font-xl;
|
||||
line-height: 200%;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.videoSearchResult {
|
||||
background-color: yellow;
|
||||
}
|
||||
|
||||
.videoResult {
|
||||
&Item {
|
||||
background-color: black;
|
||||
display: flex;
|
||||
justify-items: stretch;
|
||||
flex-wrap: none;
|
||||
}
|
||||
&Preview {
|
||||
display: inline-block;
|
||||
width: auto;
|
||||
height: auto;
|
||||
}
|
||||
&Info {
|
||||
background-color: white;
|
||||
box-sizing: border-box;
|
||||
flex: 1;
|
||||
padding-left: 1em;
|
||||
padding-right: 1em;
|
||||
}
|
||||
&Title {
|
||||
@include ms-fontSize-xl;
|
||||
margin-bottom: 9px;
|
||||
}
|
||||
&Description {
|
||||
// background-color: yellow;
|
||||
@include ms-fontSize-m;
|
||||
max-height: 5*14px*1.1;
|
||||
overflow: hidden;
|
||||
}
|
||||
&Author {
|
||||
background-color: white;
|
||||
a {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
flex-wrap: nowrap;
|
||||
width: auto;
|
||||
flex-direction: row;
|
||||
text-decoration: none;
|
||||
margin-top: 0.5em;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
&Image {
|
||||
border-radius: 50%;
|
||||
border: 1px gray solid;
|
||||
display: inline-block;
|
||||
}
|
||||
&Name {
|
||||
@include ms-fontSize-mPlus;
|
||||
line-height: 30px;
|
||||
display: inline-block;
|
||||
align-self: flex-start;
|
||||
margin-left: 0.5em;
|
||||
color: gray;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// .videoSearchBar {
|
||||
// background-color: transparent;
|
||||
// display: flex-box;
|
||||
// margin-bottom: 0.5em;
|
||||
// :global {
|
||||
// .n8d-searchbox {
|
||||
// width: calc(100% - 90px);
|
||||
// display: inline-block;
|
||||
// }
|
||||
// .n8d-searchbutton {
|
||||
// width: 90px;
|
||||
// margin: 0;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
.videoSearchBar {
|
||||
display: flex;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
.n8dSearchbox {
|
||||
width: calc(100% - 90px);
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.n8dSearchbutton {
|
||||
width: 90px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.n8dSavebutton {
|
||||
margin: 0;
|
||||
white-space: nowrap;
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
import * as React from 'react';
|
||||
import styles from './Vimeo.module.scss';
|
||||
import { IVimeoProps } from './IVimeoProps';
|
||||
import { escape } from '@microsoft/sp-lodash-subset';
|
||||
|
||||
import VimeoEntry from './VimeoEntry';
|
||||
import VimeoSearch from './VimeoSearch';
|
||||
|
||||
import VimeoPlayer from './VimeoPlayer';
|
||||
|
||||
export default class Vimeo extends React.Component<IVimeoProps, {}> {
|
||||
|
||||
constructor(props) {
|
||||
|
||||
super(props);
|
||||
|
||||
}
|
||||
|
||||
private onSave(item) {
|
||||
|
||||
this.props.onSave(item);
|
||||
|
||||
}
|
||||
|
||||
private handleModes(editMode) {
|
||||
|
||||
if (editMode) {
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1>Search Vimeo</h1>
|
||||
<VimeoSearch httpClient={this.props.httpClient} onSave={this.onSave.bind(this)} />
|
||||
</div>
|
||||
);
|
||||
|
||||
} else {
|
||||
|
||||
return (<VimeoPlayer playbackVideoUrl={this.props.VimeoUrl} />);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public render(): React.ReactElement<IVimeoProps> {
|
||||
|
||||
let modeContent = this.handleModes(this.props.editMode);
|
||||
|
||||
return (
|
||||
<div className={styles.vimeo}>
|
||||
{modeContent}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
export interface IConstants{
|
||||
VIMEO_PLAYER: string;
|
||||
VIMEO_PREVIEW: string;
|
||||
}
|
||||
|
||||
var Constants: IConstants = {
|
||||
|
||||
VIMEO_PLAYER: "HELLO WORLD",
|
||||
VIMEO_PREVIEW: "https://www.vimeo.com"
|
||||
|
||||
};
|
||||
|
||||
export default Constants;
|
|
@ -0,0 +1,116 @@
|
|||
import * as React from 'react';
|
||||
import styles from './Vimeo.module.scss';
|
||||
import { IVimeoEntryProps } from './IVimeoEntryProps';
|
||||
import { escape } from '@microsoft/sp-lodash-subset';
|
||||
|
||||
import VimeoConstants from './VimeoConstants';
|
||||
|
||||
export interface IStateVideoEntry {
|
||||
showVideo: any;
|
||||
videoPlayer: any;
|
||||
editMode: boolean;
|
||||
}
|
||||
|
||||
|
||||
export default class VimeoEntry extends React.Component<IVimeoEntryProps, {}> {
|
||||
|
||||
|
||||
private showVideo: boolean;
|
||||
public state: IStateVideoEntry;
|
||||
|
||||
|
||||
constructor(props) {
|
||||
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
showVideo: this.props,
|
||||
videoPlayer: null,
|
||||
editMode: true
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
private previewPicture(): string {
|
||||
|
||||
let pictureUrl;
|
||||
|
||||
if (this.props !== undefined && this.props.item.pictures !== undefined && this.props.item.pictures.uri !== undefined) {
|
||||
|
||||
pictureUrl = this.props.item.pictures.sizes[2].link;
|
||||
|
||||
} else {
|
||||
|
||||
pictureUrl = "";
|
||||
|
||||
}
|
||||
|
||||
return pictureUrl;
|
||||
|
||||
}
|
||||
|
||||
private userInfo(): React.ReactElement<{}> {
|
||||
|
||||
let authorUrl = "https://www.vimeo.com/" + this.props.item.author.uri.replace('s/', '');
|
||||
|
||||
return (
|
||||
<div className={styles.videoResultAuthor}>
|
||||
<a href={authorUrl} target="_blank">
|
||||
<img src={this.props.item.author.pictures.sizes[0].link} alt="" className={styles.videoResultAuthorImage} />
|
||||
<div className={styles.videoResultAuthorName}>{this.props.item.author.name}</div>
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
public showVideos() {
|
||||
|
||||
this.props.onShowVideo(this.props.item);
|
||||
|
||||
let curShowVideo = this.state.showVideo;
|
||||
|
||||
this.state.showVideo = !curShowVideo;
|
||||
this.state.videoPlayer = this.renderVideo(!curShowVideo);
|
||||
|
||||
this.setState(this.state);
|
||||
|
||||
}
|
||||
|
||||
public renderVideo(showVideo) {
|
||||
|
||||
if (showVideo) {
|
||||
|
||||
return (
|
||||
<div>Hide Videos</div>
|
||||
);
|
||||
|
||||
} else {
|
||||
|
||||
return (
|
||||
<div>Show Videos</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public render(): React.ReactElement<IVimeoEntryProps> {
|
||||
|
||||
if (this.state.editMode) {
|
||||
|
||||
return (
|
||||
<div className={styles.videoResultItem} onClick={this.showVideos.bind(this)}>
|
||||
<div className={styles.videoResultPreview}>
|
||||
<img src={this.previewPicture()} />
|
||||
</div>
|
||||
<div className={styles.videoResultInfo}>
|
||||
<div className={styles.videoResultTitle}>{this.props.item.name}</div>
|
||||
{this.userInfo()}
|
||||
<div className={styles.videoResultDescription}>{this.props.item.description}</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
import * as React from 'react';
|
||||
import styles from './Vimeo.module.scss';
|
||||
import { escape } from '@microsoft/sp-lodash-subset';
|
||||
|
||||
import VimeoConstants from './VimeoConstants';
|
||||
|
||||
export interface IVimeoPlayerProps {
|
||||
playbackVideoUrl: any;
|
||||
}
|
||||
|
||||
export default class VimeoEntry extends React.Component<IVimeoPlayerProps, {}> {
|
||||
|
||||
public render(): React.ReactElement<{ IVimeoPlayerProps }> {
|
||||
|
||||
if (this.props.playbackVideoUrl !== null) {
|
||||
|
||||
return (
|
||||
<div className={styles.videoPlayerOuter}>
|
||||
<iframe src={this.props.playbackVideoUrl} frameBorder="0" allowFullScreen className={styles.videoPlayer} ref='videoSource'></iframe>
|
||||
</div>
|
||||
);
|
||||
|
||||
} else {
|
||||
return (<span />);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
import * as React from 'react';
|
||||
import styles from './Vimeo.module.scss';
|
||||
import { escape } from '@microsoft/sp-lodash-subset';
|
||||
import VimeoPlayer from './VimeoPlayer';
|
||||
|
||||
import VimeoConstants from './VimeoConstants';
|
||||
import { PrimaryButton } from 'office-ui-fabric-react/lib/components/Button';
|
||||
|
||||
export interface IVimeoPlayerPreviewProps {
|
||||
playbackVideo: any;
|
||||
inEditMode?: boolean;
|
||||
onAdd: Function;
|
||||
}
|
||||
|
||||
export default class VimeoPlayerPreviewEntry extends React.Component<IVimeoPlayerPreviewProps, {}> {
|
||||
|
||||
private onSaveItem(){
|
||||
|
||||
this.props.onAdd(this.props.playbackVideo);
|
||||
|
||||
}
|
||||
|
||||
public render(): React.ReactElement<{ IVimeoPlayerPreviewProps }> {
|
||||
|
||||
if (this.props.playbackVideo !== null) {
|
||||
|
||||
return (
|
||||
<div className={styles.videoPreviewPane}>
|
||||
<div className={styles.videoPlayerHeader}>Video Preview</div>
|
||||
<VimeoPlayer playbackVideoUrl={ this.props.playbackVideo.url } />.
|
||||
<div className={styles.videoPlayerToolbar}> </div>
|
||||
<PrimaryButton text="Use this video" className={styles.n8dSavebutton}
|
||||
onClick={this.onSaveItem.bind(this)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
} else {
|
||||
return (<span />);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,157 @@
|
|||
import * as React from 'react';
|
||||
import styles from './Vimeo.module.scss';
|
||||
import { IVimeoSearchProps } from './IVimeoSearchProps';
|
||||
import { IVimeoProps } from './IVimeoProps';
|
||||
import { escape } from '@microsoft/sp-lodash-subset';
|
||||
|
||||
import {
|
||||
TextField
|
||||
} from 'office-ui-fabric-react/lib/TextField';
|
||||
import {
|
||||
PrimaryButton
|
||||
} from 'office-ui-fabric-react/lib/Button';
|
||||
import { HttpClient, IHttpClientOptions, HttpClientResponse } from '@microsoft/sp-http';
|
||||
import { IVimeoEntryProps } from './IVimeoEntryProps';
|
||||
import VimeoEntry from './VimeoEntry';
|
||||
import VimeoPreviewPlayer from './VimeoPreviewPlayer';
|
||||
|
||||
export interface IVideoSearchState {
|
||||
searchValue?: string;
|
||||
videos: Array<IVimeoEntryProps>;
|
||||
currentVideo?: any;
|
||||
playerHidden: string;
|
||||
}
|
||||
|
||||
export default class VimeoSearch extends React.Component<IVimeoSearchProps, IVideoSearchState> {
|
||||
|
||||
|
||||
public state: IVideoSearchState;
|
||||
|
||||
private reqOptions: IHttpClientOptions;
|
||||
|
||||
constructor(props) {
|
||||
|
||||
super(props);
|
||||
|
||||
console.log(props.httpClient);
|
||||
|
||||
this.state = {
|
||||
searchValue: '',
|
||||
videos: [],
|
||||
currentVideo: null,
|
||||
playerHidden: 'hidden'
|
||||
};
|
||||
|
||||
this.searchVideos = this.searchVideos.bind(this);
|
||||
// this.updateSearch = this.updateSearch.bind(this);
|
||||
|
||||
const reqHeaders = new Headers();
|
||||
reqHeaders.append('Content-type', 'application/json');
|
||||
|
||||
this.reqOptions = {
|
||||
headers: reqHeaders
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
private handleSearch(value) {
|
||||
|
||||
this.state.searchValue = value;
|
||||
|
||||
this.setState(
|
||||
this.state
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
|
||||
private searchVideos(evt) {
|
||||
|
||||
this.props.httpClient
|
||||
.get(
|
||||
'https://localhost:7071/api/Search?q=' + this.state.searchValue,
|
||||
HttpClient.configurations.v1,
|
||||
this.reqOptions)
|
||||
.then(
|
||||
(response: Response): Promise<HttpClientResponse> => {
|
||||
console.log("REST API response received.");
|
||||
return response.json();
|
||||
})
|
||||
.then(
|
||||
(result: any) => {
|
||||
|
||||
console.log(result.data);
|
||||
|
||||
|
||||
this.state.videos = result.data.map((videoResultItem) => {
|
||||
|
||||
var videoPlayerUri = videoResultItem.uri.replace('videos', 'video');
|
||||
|
||||
return {
|
||||
title: videoResultItem.name,
|
||||
description: videoResultItem.description,
|
||||
url: 'https://player.vimeo.com' + videoPlayerUri,
|
||||
author: videoResultItem.user,
|
||||
pictures: videoResultItem.pictures
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
this.setState(this.state);
|
||||
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private onShowVideo(item) {
|
||||
|
||||
this.state.currentVideo = item;
|
||||
this.state.playerHidden = 'show';
|
||||
|
||||
this.setState(
|
||||
this.state
|
||||
);
|
||||
}
|
||||
|
||||
public onSave(item){
|
||||
|
||||
this.props.onSave(item);
|
||||
|
||||
}
|
||||
|
||||
public render(): React.ReactElement<IVimeoSearchProps> {
|
||||
|
||||
var videoResult = this.state.videos.map((item, index) => {
|
||||
return (
|
||||
<VimeoEntry
|
||||
key={index}
|
||||
item={item}
|
||||
onShowVideo={this.onShowVideo.bind(this)}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className={styles.videoSearchBar}>
|
||||
<TextField id="HelloWorld"
|
||||
className={styles.n8dSearchbox}
|
||||
value={this.state.searchValue}
|
||||
onChanged={this.handleSearch.bind(this)}
|
||||
autoComplete="off"
|
||||
/>
|
||||
<PrimaryButton text="Search" className={styles.n8dSearchbutton}
|
||||
onClick={this.searchVideos}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<VimeoPreviewPlayer playbackVideo={this.state.currentVideo} onAdd={this.onSave.bind(this)}></VimeoPreviewPlayer>
|
||||
|
||||
<div className={styles.videoSearchResult}>
|
||||
{videoResult}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
define([], function() {
|
||||
return {
|
||||
"PropertyPaneDescription": "Description",
|
||||
"BasicGroupName": "Group Name",
|
||||
"DescriptionFieldLabel": "VimeoUrl"
|
||||
}
|
||||
});
|
10
samples/react-azfunc-vimeo/VimeoWebPart/src/webparts/vimeo/loc/mystrings.d.ts
vendored
Normal file
10
samples/react-azfunc-vimeo/VimeoWebPart/src/webparts/vimeo/loc/mystrings.d.ts
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
declare interface IVimeoWebPartStrings {
|
||||
PropertyPaneDescription: string;
|
||||
BasicGroupName: string;
|
||||
DescriptionFieldLabel: string;
|
||||
}
|
||||
|
||||
declare module 'VimeoWebPartStrings' {
|
||||
const strings: IVimeoWebPartStrings;
|
||||
export = strings;
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"module": "commonjs",
|
||||
"jsx": "react",
|
||||
"declaration": true,
|
||||
"sourceMap": true,
|
||||
"experimentalDecorators": true,
|
||||
"skipLibCheck": true,
|
||||
"typeRoots": [
|
||||
"./node_modules/@types",
|
||||
"./node_modules/@microsoft"
|
||||
],
|
||||
"types": [
|
||||
"es6-promise",
|
||||
"webpack-env"
|
||||
],
|
||||
"lib": [
|
||||
"es5",
|
||||
"dom",
|
||||
"es2015.collection"
|
||||
]
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue