Initial Commit of Azure Function Web Part
This commit is contained in:
parent
84a89814c9
commit
e997302372
|
@ -0,0 +1,99 @@
|
|||
## Local Azure Function and SPFx Web Part Development
|
||||
|
||||
This sample shows how to consume third party APIs through an Azure Functions by a Web Part. In this scenario Vimeo was choose as an example for 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 web site [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 needs to be entered in the local Azure function configuration.
|
||||
|
||||
```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 an 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-azfunct-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,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,26 @@
|
|||
## vimeo-web-part
|
||||
|
||||
This is where you include your WebPart documentation.
|
||||
|
||||
### Building the code
|
||||
|
||||
```bash
|
||||
git clone the repo
|
||||
npm i
|
||||
npm i -g gulp
|
||||
gulp
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
### Build options
|
||||
|
||||
gulp clean - TODO
|
||||
gulp test - TODO
|
||||
gulp serve - TODO
|
||||
gulp bundle - TODO
|
||||
gulp package-solution - TODO
|
|
@ -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