diff --git a/.babelrc b/.babelrc new file mode 100644 index 0000000..6a1d5dc --- /dev/null +++ b/.babelrc @@ -0,0 +1,13 @@ +{ + "passPerPreset": true, + "presets": [ + { + "plugins": [ + "./build/babelRelayPlugin" + ] + }, + "react", + "es2015", + "stage-0" + ] +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6d65275 --- /dev/null +++ b/.gitignore @@ -0,0 +1,52 @@ +# Packages # +############ +# it's better to unpack these files and commit the raw source +# git has its own built in compression methods +*.7z +*.dmg +*.gz +*.iso +*.jar +*.rar +*.tar +*.zip + +# Logs and databases # +###################### +*.log +*.sql +*.sqlite + +# OS generated files # +###################### +.DS_Store +.DS_Store? +._* +.Spotlight-V100 +.Trashes +ehthumbs.db +Thumbs.db + +# Build Systems # +################# +node_modules/ +bower_components/ +.sass-cache +*.css.map +dist/ + +# Random # +########## +.Ulysses-Group.plist +.idea/ +*.sublime-project +*.sublime-workspace +npm-debug.log + +# Project Specific # +#################### +sandbox/ +src/constants/config.js +src/constants/client_secret.json +src/data/schema.graphql +src/data/schema.json \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..779606a --- /dev/null +++ b/LICENSE @@ -0,0 +1,31 @@ +BSD License + +For Relay Starter Kit software + +Copyright (c) 2013-2015, Facebook, Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + * Neither the name Facebook nor the names of its contributors may be used to + endorse or promote products derived from this software without specific + prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/PATENTS b/PATENTS new file mode 100644 index 0000000..be39fb3 --- /dev/null +++ b/PATENTS @@ -0,0 +1,33 @@ +Additional Grant of Patent Rights Version 2 + +"Software" means the Relay Starter Kit software distributed by Facebook, Inc. + +Facebook, Inc. ("Facebook") hereby grants to each recipient of the Software +("you") a perpetual, worldwide, royalty-free, non-exclusive, irrevocable +(subject to the termination provision below) license under any Necessary +Claims, to make, have made, use, sell, offer to sell, import, and otherwise +transfer the Software. For avoidance of doubt, no license is granted under +Facebook's rights in any patent claims that are infringed by (i) modifications +to the Software made by you or any third party or (ii) the Software in +combination with any software or other technology. + +The license granted hereunder will terminate, automatically and without notice, +if you (or any of your subsidiaries, corporate affiliates or agents) initiate +directly or indirectly, or take a direct financial interest in, any Patent +Assertion: (i) against Facebook or any of its subsidiaries or corporate +affiliates, (ii) against any party if such Patent Assertion arises in whole or +in part from any software, technology, product or service of Facebook or any of +its subsidiaries or corporate affiliates, or (iii) against any party relating +to the Software. Notwithstanding the foregoing, if Facebook or any of its +subsidiaries or corporate affiliates files a lawsuit alleging patent +infringement against you in the first instance, and you respond by filing a +patent infringement counterclaim in that lawsuit against that party that is +unrelated to the Software, the license granted hereunder will not terminate +under section (i) of this paragraph due to such counterclaim. + +A "Necessary Claim" is a claim of a patent owned by Facebook that is +necessarily infringed by the Software standing alone. + +A "Patent Assertion" is any lawsuit or other action alleging direct, indirect, +or contributory infringement or inducement to infringe any patent, including a +cross-claim or counterclaim. diff --git a/README.md b/README.md new file mode 100644 index 0000000..1923981 --- /dev/null +++ b/README.md @@ -0,0 +1,34 @@ +# Relay Starter Kit + +This kit includes an app server, a GraphQL server, and a transpiler that you can use to get started building an app with Relay. For a walkthrough, see the [Relay tutorial](https://facebook.github.io/relay/docs/tutorial.html). + +## Installation + +``` +npm install +``` + +## Running + +Start a local server: + +``` +npm start +``` + +## Developing + +Any changes you make to files in the `js/` directory will cause the server to +automatically rebuild the app and refresh your browser. + +If at any time you make changes to `data/schema.js`, stop the server, +regenerate `data/schema.json`, and restart the server: + +``` +npm run update-schema +npm start +``` + +## License + +Relay Starter Kit is [BSD licensed](./LICENSE). We also provide an additional [patent grant](./PATENTS). diff --git a/package.json b/package.json new file mode 100644 index 0000000..ed16cab --- /dev/null +++ b/package.json @@ -0,0 +1,40 @@ +{ + "name": "relay-starter-kit", + "private": true, + "description": "A quick way to get up and running with Relay", + "repository": "facebook/relay-starter-kit", + "version": "0.1.0", + "scripts": { + "start": "babel-node ./server.js", + "update-schema": "babel-node ./scripts/updateSchema.js" + }, + "dependencies": { + "babel-core": "^6.5.2", + "babel-loader": "6.2.3", + "babel-polyfill": "6.5.0", + "babel-preset-es2015": "6.5.0", + "babel-preset-react": "6.5.0", + "babel-preset-stage-0": "6.5.0", + "babel-relay-plugin": "0.7.3", + "classnames": "2.2.3", + "express": "4.13.4", + "express-graphql": "0.4.9", + "graphql": "0.4.17", + "graphql-relay": "0.3.6", + "react": "0.14.7", + "react-dom": "0.14.7", + "react-relay": "0.7.3", + "webpack": "1.12.13", + "webpack-dev-server": "1.14.1" + }, + "devDependencies": { + "babel-cli": "6.5.1", + "babel-plugin-transform-class-properties": "^6.6.0", + "file-loader": "^0.8.5", + "googleapis": "^4.0.0", + "jsonfile": "^2.2.3", + "node-schedule": "^1.1.0", + "url-loader": "^0.5.7", + "webpack-isomorphic-tools": "^2.2.44" + } +} diff --git a/public/index.html b/public/index.html new file mode 100644 index 0000000..343d0c3 --- /dev/null +++ b/public/index.html @@ -0,0 +1,18 @@ + + + + + + Alex Cory + + +
+ + + + + \ No newline at end of file diff --git a/scripts/updateSchema.js b/scripts/updateSchema.js new file mode 100755 index 0000000..0071700 --- /dev/null +++ b/scripts/updateSchema.js @@ -0,0 +1,29 @@ +#!/usr/bin/env babel-node --optional es7.asyncFunctions + +import fs from 'fs'; +import path from 'path'; +import { Schema } from '../src/data/schema'; +import { graphql } from 'graphql'; +import { introspectionQuery, printSchema } from 'graphql/utilities'; + +// Save JSON of full schema introspection for Babel Relay Plugin to use +(async () => { + var result = await (graphql(Schema, introspectionQuery)); + if (result.errors) { + console.error( + 'ERROR introspecting schema: ', + JSON.stringify(result.errors, null, 2) + ); + } else { + fs.writeFileSync( + path.join(__dirname, '../src/data/schema.json'), + JSON.stringify(result, null, 2) + ); + } +})(); + +// Save user readable type system shorthand of schema +fs.writeFileSync( + path.join(__dirname, '../src/data/schema.graphql'), + printSchema(Schema) +); diff --git a/server.js b/server.js new file mode 100644 index 0000000..060f8cd --- /dev/null +++ b/server.js @@ -0,0 +1,80 @@ +import express from 'express'; +import graphQLHTTP from 'express-graphql'; +import path from 'path'; +import webpack from 'webpack'; +import WebpackDevServer from 'webpack-dev-server'; +import {Schema} from './src/data/schema'; +// var Webpack_isomorphic_tools_plugin = require('webpack-isomorphic-tools/plugin') + +const APP_PORT = 3000; +const GRAPHQL_PORT = 8080; + +// Expose a GraphQL endpoint +var graphQLServer = express(); +graphQLServer.use('/', graphQLHTTP({ + graphiql: true, + pretty: true, + schema: Schema, +})); +graphQLServer.listen(GRAPHQL_PORT, () => console.log( + `GraphQL Server is now running on http://localhost:${GRAPHQL_PORT}` +)); + + + +// var webpack_isomorphic_tools_plugin = +// // webpack-isomorphic-tools settings reside in a separate .js file +// // (because they will be used in the web server code too). +// new Webpack_isomorphic_tools_plugin(require('./webpack-isomorphic-tools-configuration')) +// // also enter development mode since it's a development webpack configuration +// // (see below for explanation) +// .development() + +// Serve the Relay app +var compiler = webpack({ + context: __dirname + '/src', + entry: path.resolve(__dirname, 'src', 'app.jsx'), + output: { + filename: 'app.js', + path: '/', + publicPath: '/' + }, + module: { + loaders: [ + { + test: /.js[x]?$/, + loader: 'babel-loader', + exclude: /node_modules/ + }, + /* For Loading Images in CSS/SCSS Files */ + { + test: /\.jpe?g$|\.gif$|\.png$|\.ico|\.svg$|\.woff$|\.ttf$|.mp4$/, + loader: 'url-loader?limit=8192' + // loader: 'file-loader?name=images/[name].[ext]' + }, + /* For loading images and video files */ + // { + // test: webpack_isomorphic_tools_plugin.regular_expression('images'), + // loader: 'url-loader?limit=10240', // any image below or equal to 10K will be converted to inline base64 instead + // } + ] + }, + resolve: { + moduleDirectories: ['node_modules', './src'], + extensions: ['', '.js', '.json', '.jsx'] + }, + // plugins: [ + // webpack_isomorphic_tools_plugin + // ] +}); +var app = new WebpackDevServer(compiler, { + contentBase: '/public/', + proxy: {'/graphql': `http://localhost:${GRAPHQL_PORT}`}, + publicPath: '/src/', + stats: {colors: true} +}); +// Serve static resources +app.use('/', express.static(path.resolve(__dirname, 'public'))); +app.listen(APP_PORT, () => { + console.log(`App is now running on http://localhost:${APP_PORT}`); +}); diff --git a/src/apis/google-drive/GoogleDoc.js b/src/apis/google-drive/GoogleDoc.js new file mode 100644 index 0000000..9fcc7a0 --- /dev/null +++ b/src/apis/google-drive/GoogleDoc.js @@ -0,0 +1,41 @@ +// var google = require('googleapis'); +// import authorize from './authorize.js' + +export default class GoogleDoc { + constructor(data) { + this._fileId = data.id || null + this._fileUrl = data.alternateLink || null + this._rawUrl = this.fileId ? `https://drive.google.com/uc?export=view&id=${this.fileId}` : null; + this._thumb = data.null + this._description = data.description || null + this._downloadUrl = data.webContentLink || null + + } + + get fileId() { + return this._fileId + } + + get fileUrl() { + return this._fileUrl + } + + get rawUrl() { + return this._rawUrl + } + + get thumb() { + return this._thumb + } + + get description() { + return this._description + } + + + // set fileId(fileId) { + // this._fileId = fileId + // } + + // get data(auth, ) +} \ No newline at end of file diff --git a/src/apis/google-drive/cache/googleDocs.json b/src/apis/google-drive/cache/googleDocs.json new file mode 100644 index 0000000..64038ef --- /dev/null +++ b/src/apis/google-drive/cache/googleDocs.json @@ -0,0 +1 @@ +[{"id":"0B5LhVy_zkvWqSTMxVTA3LWhRanc","title":"scubadiving1.jpg","parents":[{"id":"0B5LhVy_zkvWqc2N1ZTNPeFFFLTA"}],"imageMediaMetadata":{"width":960,"height":720}},{"id":"0B5LhVy_zkvWqTWF6aXN4c3liRk0","title":"backpacking1.jpg","parents":[{"id":"0B5LhVy_zkvWqc2N1ZTNPeFFFLTA"}],"imageMediaMetadata":{"width":960,"height":720}},{"id":"0B5LhVy_zkvWqZW9kcklCQWlrWXM","title":"kayaking1.mp4","parents":[{"id":"0B5LhVy_zkvWqc2N1ZTNPeFFFLTA"}],"videoMediaMetadata":{"width":1280,"height":720,"durationMillis":"99683"}},{"id":"0B5LhVy_zkvWqcnhwMkJBR2JzelE","title":"mountainbiking.mov","parents":[{"id":"0B5LhVy_zkvWqc2N1ZTNPeFFFLTA"}],"videoMediaMetadata":{"width":1920,"height":1080,"durationMillis":"30561"}},{"id":"0B5LhVy_zkvWqMnEtNzJ5ekFEWlk","title":"ziplining1.jpg","parents":[{"id":"0B5LhVy_zkvWqc2N1ZTNPeFFFLTA"}],"imageMediaMetadata":{"width":2048,"height":1356}},{"id":"0B5LhVy_zkvWqUTFGVDQzeUhocE0","title":"scubadiving2.jpg","parents":[{"id":"0B5LhVy_zkvWqc2N1ZTNPeFFFLTA"}],"imageMediaMetadata":{"width":1643,"height":918}},{"id":"0B5LhVy_zkvWqemJ4aThKVTBjOG8","title":"wakeboarding2.mp4","parents":[{"id":"0B5LhVy_zkvWqc2N1ZTNPeFFFLTA"}],"videoMediaMetadata":{"width":1280,"height":720,"durationMillis":"23382"}},{"id":"0B5LhVy_zkvWqNm1PWkdtOThlZWc","title":"mtbiking.JPG","description":"Mountain biking at Town Run in Indianapolis, IN","parents":[{"id":"0B5LhVy_zkvWqc2N1ZTNPeFFFLTA"}],"imageMediaMetadata":{"width":2048,"height":2048}},{"id":"0B5LhVy_zkvWqV3B1WThoRWZQZzg","title":"skydiving1.VOB","parents":[{"id":"0B5LhVy_zkvWqc2N1ZTNPeFFFLTA"}],"videoMediaMetadata":{"width":720,"height":480,"durationMillis":"272806"}},{"id":"0B5LhVy_zkvWqaUNyVmhFZzRIUzA","title":"lostcoast.jpg","parents":[{"id":"0B5LhVy_zkvWqc2N1ZTNPeFFFLTA"}],"imageMediaMetadata":{"width":640,"height":640}},{"id":"0B5LhVy_zkvWqMEhfOUNNamkyUGs","title":"wakeboarding1.mp4","parents":[{"id":"0B5LhVy_zkvWqc2N1ZTNPeFFFLTA"}],"videoMediaMetadata":{"width":1280,"height":720,"durationMillis":"64992"}},{"id":"0B5LhVy_zkvWqVUFaRkFoRmxmUHM","title":"skateboarding1.MOV","parents":[{"id":"0B5LhVy_zkvWqc2N1ZTNPeFFFLTA"}],"videoMediaMetadata":{"width":1920,"height":1080,"durationMillis":"7500"}},{"id":"0B5LhVy_zkvWqRHNaOWFGYnFIWTA","title":"wakeboarding2.jpg","parents":[{"id":"0B5LhVy_zkvWqc2N1ZTNPeFFFLTA"}],"imageMediaMetadata":{"width":720,"height":540}},{"id":"0B5LhVy_zkvWqYmZhSFBtNGlKVlU","title":"snowboardingme.jpg","parents":[{"id":"0B5LhVy_zkvWqc2N1ZTNPeFFFLTA"}],"imageMediaMetadata":{"width":1080,"height":1080}},{"id":"0B5LhVy_zkvWqMTVjSURpNVFaOGM","title":"wakeboarding.jpg","parents":[{"id":"0B5LhVy_zkvWqc2N1ZTNPeFFFLTA"}],"imageMediaMetadata":{"width":612,"height":612}},{"id":"0B5LhVy_zkvWqamx5aFZTZExjb1E","title":"snowboarding1.MOV","description":"360 at Heavenly Ski Resort in South Lake Tahoe","parents":[{"id":"0B5LhVy_zkvWqc2N1ZTNPeFFFLTA"}],"videoMediaMetadata":{"width":1920,"height":1080,"durationMillis":"8505"}}] diff --git a/src/apis/google-drive/cache/snowboarding123.jpg b/src/apis/google-drive/cache/snowboarding123.jpg new file mode 100644 index 0000000..5f44fb1 Binary files /dev/null and b/src/apis/google-drive/cache/snowboarding123.jpg differ diff --git a/src/apis/google-drive/index.js b/src/apis/google-drive/index.js new file mode 100644 index 0000000..9e49726 --- /dev/null +++ b/src/apis/google-drive/index.js @@ -0,0 +1,165 @@ +let fs = require('fs') +let async = require("async") +let ggl = require('googleapis') +import { google } from '../../constants/config.js' +import GoogleDoc from './GoogleDoc.js' +import jsonfile from 'jsonfile' + +async function getGoogleDocsData() { + let filesData = await getFilesFromGoogleDriveFolder('0B5LhVy_zkvWqc2N1ZTNPeFFFLTA') + let cache = './cache/googleDocs.json' + await cacheData(filesData, cache) /* TODO: use a cron job for this every hour */ + // let data = await getCachedData(cache) + console.log(filesData); + // return await getCachedData(cache) + // console.log(await getCachedData(cache)) +} +getGoogleDocsData() +// let googleDocsData = getGoogleDocsData() +// export default googleDocsData + +/* + * To Test: + * 1. remove `export default` from getFiles() + * 2. uncomment `console.log(files)` + * 3. comment out `return files` + * 4. uncomment `getFiles()` + * 5. run `babel-node connect2driveAsyncAwait.js` in your terminal + */ +async function getFilesFromGoogleDriveFolder(folderId) { + let drive = await setupDrive() + let fileIds = await getFileIdsFromFolder(folderId, drive) + let files = await getFileData(fileIds, drive) + // console.log(files) + return files +} +// getFilesFromGoogleDriveFolder('0B5LhVy_zkvWqc2N1ZTNPeFFFLTA') + + +function setupDrive() { + let drive + let OAuth2 = ggl.auth.OAuth2 + let oauth2Client = new OAuth2(google.CLIENT_ID, google.CLIENT_SECRET, google.REDIRECT_URL[0]) + let TOKEN_DIR = (process.env.HOME || process.env.HOMEPATH || process.env.USERPROFILE) + '/.credentials/' + let TOKEN_PATH = TOKEN_DIR + 'drive-nodejs-quickstart.json' + + return new Promise((accept, reject) => { + + fs.readFile(TOKEN_PATH, (err, contents) => { + if (err) { + console.log('Error loading client secret file: ' + err) + return + } + + let token = JSON.parse(contents) + // refreshes the access token if it needs to be + oauth2Client.setCredentials(token) + // authenticates us so we can make requests + drive = ggl.drive({ version: 'v2', auth: oauth2Client }) + + return accept(drive) + }) + + }) +} + +function getFileIdsFromFolder(folderId, drive) { + + let fileIds = [] + + return new Promise((accept, reject) => { + + let folderOptions = { + folderId: folderId, + fields: 'items(childLink,id)' + } + + drive.children.list(folderOptions, (err, response) => { + if (err) { + console.log('The API returned an error: ' + err) + return reject() + } + + let files = response.items + + if (files.length == 0) { + console.log('No files found.') + + } else { + // return accept(files) + // console.log(files); + + for (let file of files) { + fileIds.push(file.id); + } + return accept(fileIds) + } + }) + }) +} + +function getFileData(fileIds, drive) { + return new Promise((accept, reject) => { + let files = [] + let batchSize = 9 // amount of requests, aka concurrency + let waitTime = 1000 // 1 second + + // Throttling the requests made to Google Drive API to 10/sec + let taskQueue = async.queue((task, callback) => { + + let options = { + fileId: task.fileId, + // fields: 'alternateLink,description,downloadUrl,fileSize,id,imageMediaMetadata(height,width),thumbnail,thumbnailLink,title,webContentLink,webViewLink' + fields: 'description,id,imageMediaMetadata(height,width),parents/id,thumbnail/image,thumbnailLink,title,videoMediaMetadata' + } + + // Making the request to Google Drive to get the data for each file + drive.files.get(options, (err, response) => { + if (err) { + console.log('The API returned an error: ' + err) + return reject() + } + + let data = response + // console.log(data); + files.push( + data + // new GoogleDoc(data) + ) + + }) + + // Wait 1 second + setTimeout(() => { + callback() + }, waitTime) + + // run `n` number of api calls each second (n = 9) + }, batchSize) + + for (let fileId of fileIds) { + // Push all the file id's into the task queue + taskQueue.push({fileId}, (err) => { + // Done + if (err) { + console.log(err) + } + }) + } + + // when all the tasks are done, resolve the promise + taskQueue.drain = () => { + return accept(files) + } + }) +} + +function cacheData(data, location) { + jsonfile.writeFile(location, data, (err) => { + if (err) console.error(err) + }) +} + +function getCachedData(location) { + return jsonfile.readFileSync(location) +} \ No newline at end of file diff --git a/src/apis/scrape-medium/cache/blogPosts.json b/src/apis/scrape-medium/cache/blogPosts.json new file mode 100644 index 0000000..9ebe673 --- /dev/null +++ b/src/apis/scrape-medium/cache/blogPosts.json @@ -0,0 +1 @@ +[{"title":"A new post","date":"21 hrs ago","url":"https://medium.com/wappy-spayberry/a-new-post-92c13b3afb9?source=latest---------1"},{"title":"test","date":"yesterday","url":"https://medium.com/@fasthacks/test-96894f04e8ee?source=latest---------2"},{"title":"Test 1","date":"Feb 16","url":"https://medium.com/@fasthacks/test-1-22865fad94d8?source=latest---------3"}] \ No newline at end of file diff --git a/src/app.jsx b/src/app.jsx new file mode 100644 index 0000000..a7f3637 --- /dev/null +++ b/src/app.jsx @@ -0,0 +1,15 @@ +import 'babel-polyfill'; + +import App from './components/App.jsx'; +import AppHomeRoute from './routes/AppHomeRoute.jsx'; +import React from 'react'; +import ReactDOM from 'react-dom'; +import Relay from 'react-relay'; + +ReactDOM.render( + , + document.getElementById('root') +); diff --git a/src/components/App.jsx b/src/components/App.jsx new file mode 100644 index 0000000..0411807 --- /dev/null +++ b/src/components/App.jsx @@ -0,0 +1,40 @@ +import React from 'react'; +import Relay from 'react-relay'; +import MediumPosts from './MediumPosts.jsx' +import PlayList from './PlayList.jsx'; + +class App extends React.Component { + render() { + // console.log('App: ', this.props.viewer); + return ( +
+

App

+ + + + {/* */} +
+ ); + } +} + +export default Relay.createContainer(App, { + fragments: { + viewer: () => Relay.QL` + fragment on User { + ${MediumPosts.getFragment('viewer')} + ${PlayList.getFragment('viewer')} + } + `, + }, +}); diff --git a/src/components/MediumPosts.jsx b/src/components/MediumPosts.jsx new file mode 100644 index 0000000..fac6c0a --- /dev/null +++ b/src/components/MediumPosts.jsx @@ -0,0 +1,39 @@ +import React from 'react'; +import Relay from 'react-relay'; + +class MediumPosts extends React.Component { + render() { + // console.log('MediumPosts: ', this.props); + return ( +
+

Medium Post list

+ +
+ ); + } +} + +export default Relay.createContainer(MediumPosts, { + fragments: { + viewer: () => Relay.QL` + fragment on User { + mediumPosts(first: 10) { + edges { + node { + id, + title, + date, + url + } + } + } + } + `, + }, +}); \ No newline at end of file diff --git a/src/components/PlayList.jsx b/src/components/PlayList.jsx new file mode 100644 index 0000000..da6f505 --- /dev/null +++ b/src/components/PlayList.jsx @@ -0,0 +1,57 @@ +import React from 'react'; +import Relay from 'react-relay'; + +class PlayList extends React.Component { + // static propTypes = { + // name: React.PropTypes.string, + // }; + + constructor(props) { + super(props); + } + + render() { + // const videos = { + // brian: require("file!./wakeboarding2.mp4") + // }; + // console.log(videos); + // console.log('Play: ', this.props.viewer); + return ( +
+

Play list

+ +
+ ); + } +} + +export default Relay.createContainer(PlayList, { + fragments: { + viewer: () => Relay.QL` + fragment on User { + googleDocs(first: 100) { + edges { + node { + id + url + } + } + } + } + `, + }, +}); \ No newline at end of file diff --git a/src/components/snowboarding1.MOV b/src/components/snowboarding1.MOV new file mode 100644 index 0000000..7cc34f9 Binary files /dev/null and b/src/components/snowboarding1.MOV differ diff --git a/src/components/wakeboarding2.mp4 b/src/components/wakeboarding2.mp4 new file mode 100644 index 0000000..1617227 Binary files /dev/null and b/src/components/wakeboarding2.mp4 differ diff --git a/src/data/database.js b/src/data/database.js new file mode 100644 index 0000000..74ce725 --- /dev/null +++ b/src/data/database.js @@ -0,0 +1,65 @@ +/** + * Copyright (c) 2015, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +let mediumPostsData = require('../apis/scrape-medium/cache/blogPosts.json') +let googleDocsData = require('../apis/google-drive/cache/googleDocs.json') + +// Model types +class MediumPost {} +class GoogleDoc { + constructor(id, name, caption, folderId, thumb) { + this.id = id || null + this.name = name || null + this.url = folderId && name ? `https://googledrive.com/host/${folderId}/${name}` : null + // this.rawUrl = id ? `https://googledrive.com/host/${this.folderId}/[file name].jpg` + // this.rawUrl = id ? `https://drive.google.com/uc?export=view&id=[id]` : null; + this.folderId = folderId || null + this.thumb = thumb || null + this.caption = caption || null + } +} +class User {} + +var mediumPosts = mediumPostsData.map((mediumPostData, i) => { + var mediumPost = new MediumPost(); + mediumPost.title = mediumPostData.title; + mediumPost.id = `${i}`; + mediumPost.date = `${mediumPostData.date}`; + mediumPost.url = mediumPostData.url; + return mediumPost; +}) + +var googleDocs = googleDocsData.map((data, i) => { + return new GoogleDoc( + data.id, // id + data.title, // name + data.description, // caption + data.parents[0].id, // folderId + data.thumbnailLink // thumb + + ) +}) +// console.log(googleDocs); + +// Mock data +var viewer = new User(); +viewer.id = '1'; +viewer.name = 'Anonymous'; + +module.exports = { + // Export methods that your schema can use to interact with your database + getMediumPost: (id) => mediumPosts.find(post => post.id === id), + getMediumPosts: () => mediumPosts, + getGoogleDoc: (id) => googleDocs.find(doc => doc.id === id), + getGoogleDocs: () => googleDocs, + getUser: (id) => id === viewer.id ? viewer : null, + getViewer: () => viewer, + MediumPost, + GoogleDoc, + User, +}; diff --git a/src/data/schema.js b/src/data/schema.js new file mode 100644 index 0000000..e0b0fcd --- /dev/null +++ b/src/data/schema.js @@ -0,0 +1,192 @@ +/** + * Copyright (c) 2015, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +import { + GraphQLBoolean, + GraphQLFloat, + GraphQLID, + GraphQLInt, + GraphQLList, + GraphQLNonNull, + GraphQLObjectType, + GraphQLSchema, + GraphQLString, +} from 'graphql'; + +import { + connectionArgs, + connectionDefinitions, + connectionFromArray, + fromGlobalId, + globalIdField, + mutationWithClientMutationId, + nodeDefinitions, +} from 'graphql-relay'; + +import { + // Import methods that your schema can use to interact with your database + MediumPost, + getMediumPost, + getMediumPosts, + GoogleDoc, + getGoogleDoc, + getGoogleDocs, + User, + getUser, + getViewer +} from './database'; + +/** + * We get the node interface and field from the Relay library. + * + * The first method defines the way we resolve an ID to its object. + * The second defines the way we resolve an object to its GraphQL type. + */ +let {nodeInterface, nodeField} = nodeDefinitions( + (globalId) => { + let {type, id} = fromGlobalId(globalId); + if (type === 'MediumPost') { + return getMediumPost(id); + } else if (type === 'GoogleDoc') { + return getGoogleDoc(id); + } else if (type === 'User') { + return getUser(id); + } else { + return null; + } + }, + (obj) => { + if (obj instanceof MediumPost) { + return mediumPostType; + } else if (obj instanceof GoogleDoc) { + return googleDocType; + } else if (obj instanceof User) { + return userType; + } else { + return null; + } + } +); + +/** + * Define your own types here + */ + +let mediumPostType = new GraphQLObjectType({ + name: 'MediumPost', + description: 'A story/post from Alex\'s Medium!', + fields: () => ({ + id: globalIdField('MediumPost'), + title: { + type: GraphQLString, + description: 'The title of the Medium post', + }, + date: { + type: GraphQLString, /* TODO: change this to int I think */ + description: 'The date when the blog was published' + }, + url: { + type: GraphQLString, + description: 'The url leading to the medium post' + } + }), + interfaces: [nodeInterface], +}); + +let googleDocType = new GraphQLObjectType({ + name: 'GoogleDoc', + description: 'A file from Google Drive!', + fields: () => ({ + id: globalIdField('GoogleDoc'), + url: { + type: GraphQLString, + description: 'The url linking directly to the image or video' + }, + thumb: { + type: GraphQLString, + description: 'Image thumbnail', + }, + caption: { + type: GraphQLString, + description: 'A short description of what\'s going on in the image/video' + }, + downloadUrl: { + type: GraphQLString, + description: 'This is a url that will instantly download the file' + } + }), + interfaces: [nodeInterface], +}); + +let userType = new GraphQLObjectType({ + name: 'User', + description: 'A person who uses our app', + fields: () => ({ + id: globalIdField('User'), + mediumPosts: { + type: mediumPostConnection, + description: 'A collection of Alex\'s Medium posts', + args: connectionArgs, + resolve: (_, args) => connectionFromArray(getMediumPosts(), args) + }, + googleDocs: { + type: googleDocConnection, + description: 'A person\'s google drive files from within the specified folder', + args: connectionArgs, + resolve: (_, args) => connectionFromArray(getGoogleDocs(), args), + } + }), + interfaces: [nodeInterface], +}); + +/** + * Define your own connection types here + */ +let {connectionType: mediumPostConnection} = + connectionDefinitions({name: 'MediumPost', nodeType: mediumPostType}); + +let {connectionType: googleDocConnection} = + connectionDefinitions({name: 'GoogleDoc', nodeType: googleDocType}); + +/** + * This is the type that will be the root of our query, + * and the entry point into our schema. + */ +let queryType = new GraphQLObjectType({ + name: 'Query', + fields: () => ({ + node: nodeField, + // Add your own root fields here + viewer: { + type: userType, + resolve: () => getViewer(), + } + }), +}); + +/** + * This is the type that will be the root of our mutations, + * and the entry point into performing writes in our schema. + */ +let mutationType = new GraphQLObjectType({ + name: 'Mutation', + fields: () => ({ + // Add your own mutations here + }) +}); + +/** + * Finally, we construct our schema (whose starting query type is the query + * type we defined above) and export it. + */ +export let Schema = new GraphQLSchema({ + query: queryType, + // Uncomment the following after adding some mutation fields: + // mutation: mutationType +}); diff --git a/src/routes/AppHomeRoute.jsx b/src/routes/AppHomeRoute.jsx new file mode 100644 index 0000000..208ceca --- /dev/null +++ b/src/routes/AppHomeRoute.jsx @@ -0,0 +1,12 @@ +import Relay from 'react-relay'; + +export default class extends Relay.Route { + static queries = { + viewer: () => Relay.QL` + query { + viewer + } + ` + }; + static routeName = 'AppHomeRoute'; +} diff --git a/src/webpack-assets.json b/src/webpack-assets.json new file mode 100644 index 0000000..f4f5aac --- /dev/null +++ b/src/webpack-assets.json @@ -0,0 +1,7 @@ +{ + "javascript": { + "main": "/app.js" + }, + "styles": {}, + "assets": {} +} \ No newline at end of file diff --git a/webpack-isomorphic-tools-configuration.js b/webpack-isomorphic-tools-configuration.js new file mode 100644 index 0000000..d433187 --- /dev/null +++ b/webpack-isomorphic-tools-configuration.js @@ -0,0 +1,13 @@ +import Webpack_isomorphic_tools_plugin from 'webpack-isomorphic-tools/plugin' + +module.exports = { + assets: { + images: { + extensions: ['png', 'jpg', 'jpeg', 'gif', 'ico', 'svg'] + }, + videos: { + extensions: [ 'webm', 'mp4' ] + //, parser: WebpackIsomorphicToolsPlugin.url_loader_parser + }, + } +} \ No newline at end of file