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 (
+
+ );
+ }
+}
+
+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 (
+
+ );
+ }
+}
+
+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