├── .meteor ├── .gitignore ├── release ├── platforms ├── .id ├── .finished-upgraders ├── packages └── versions ├── .babelrc ├── client └── main.js ├── .gitignore ├── imports ├── client │ ├── modules │ │ └── core │ │ │ ├── reducers │ │ │ ├── index.js │ │ │ └── core.js │ │ │ ├── actions │ │ │ ├── index.js │ │ │ └── toast.js │ │ │ ├── index.js │ │ │ ├── containers │ │ │ ├── mainLayout.js │ │ │ ├── view_home.js │ │ │ └── toast.js │ │ │ ├── routes.js │ │ │ └── components │ │ │ ├── toast.js │ │ │ ├── mainLayout.js │ │ │ └── view_home.js │ ├── main.js │ └── configs │ │ └── context.js ├── lib │ └── collections.js └── server │ ├── main.js │ └── api │ ├── mediaGallery │ └── schema.js │ ├── poi │ └── schema.js │ ├── schema.js │ └── destination │ └── schema.js ├── server └── main.js ├── package.json └── README.md /.meteor/.gitignore: -------------------------------------------------------------------------------- 1 | local 2 | -------------------------------------------------------------------------------- /.meteor/release: -------------------------------------------------------------------------------- 1 | METEOR@1.4.1.1 2 | -------------------------------------------------------------------------------- /.meteor/platforms: -------------------------------------------------------------------------------- 1 | server 2 | browser 3 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ "meteor" ] 3 | } -------------------------------------------------------------------------------- /client/main.js: -------------------------------------------------------------------------------- 1 | import '../imports/client/main'; -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | settings.json 3 | startup.sh 4 | .idea/ -------------------------------------------------------------------------------- /imports/client/modules/core/reducers/index.js: -------------------------------------------------------------------------------- 1 | 2 | import core from './core'; 3 | 4 | export default { 5 | core 6 | }; -------------------------------------------------------------------------------- /server/main.js: -------------------------------------------------------------------------------- 1 | import { Meteor } from 'meteor/meteor'; 2 | import '/imports/server/main'; 3 | 4 | Meteor.startup(() => { 5 | 6 | }); 7 | -------------------------------------------------------------------------------- /imports/client/modules/core/actions/index.js: -------------------------------------------------------------------------------- 1 | import toast from './toast'; 2 | 3 | 4 | const actions = { 5 | toast 6 | }; 7 | 8 | 9 | export default actions; 10 | -------------------------------------------------------------------------------- /imports/client/modules/core/index.js: -------------------------------------------------------------------------------- 1 | 2 | import routes from './routes'; 3 | import actions from './actions'; 4 | import reducers from './reducers'; 5 | 6 | 7 | export default { 8 | routes, 9 | actions, 10 | reducers 11 | }; -------------------------------------------------------------------------------- /imports/lib/collections.js: -------------------------------------------------------------------------------- 1 | import {Mongo} from 'meteor/mongo'; 2 | 3 | export const MediaGallery = new Mongo.Collection('mediaGallery'); 4 | export const Destination = new Mongo.Collection('destination'); 5 | export const Poi = new Mongo.Collection('poi'); 6 | -------------------------------------------------------------------------------- /.meteor/.id: -------------------------------------------------------------------------------- 1 | # This file contains a token that is unique to your project. 2 | # Check it into your repository along with the rest of this directory. 3 | # It can be used for purposes such as: 4 | # - ensuring you don't accidentally deploy one app on top of another 5 | # - providing package authors with aggregated statistics 6 | 7 | 7n1xegljkhlggns11a -------------------------------------------------------------------------------- /.meteor/.finished-upgraders: -------------------------------------------------------------------------------- 1 | # This file contains information which helps Meteor properly upgrade your 2 | # app when you run 'meteor update'. You should check it into version control 3 | # with your project. 4 | 5 | notices-for-0.9.0 6 | notices-for-0.9.1 7 | 0.9.4-platform-file 8 | notices-for-facebook-graph-api-2 9 | 1.2.0-standard-minifiers-package 10 | 1.2.0-meteor-platform-split 11 | 1.2.0-cordova-changes 12 | 1.2.0-breaking-changes 13 | 1.3.0-split-minifiers-package 14 | 1.4.0-remove-old-dev-bundle-link 15 | 1.4.1-add-shell-server-package -------------------------------------------------------------------------------- /imports/client/main.js: -------------------------------------------------------------------------------- 1 | import {initContext} from './configs/context'; 2 | import {createApp} from 'mantra-core'; 3 | import injectTapEventPlugin from 'react-tap-event-plugin'; 4 | 5 | 6 | //modules 7 | import coreModule from './modules/core'; 8 | 9 | 10 | const reducers = ({ 11 | ...coreModule.reducers 12 | }); 13 | 14 | 15 | // init context 16 | const context = initContext({ reducers }); 17 | 18 | //create client 19 | const app = createApp(context); 20 | app.loadModule(coreModule); 21 | 22 | app.init(); 23 | injectTapEventPlugin(); 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /imports/client/modules/core/containers/mainLayout.js: -------------------------------------------------------------------------------- 1 | import {useDeps, composeAll, compose} from 'mantra-core'; 2 | import mainLayout from '../components/mainLayout'; 3 | 4 | 5 | const onPropsChange = ({ context }, onData) => { 6 | //we need to pass the Store & Client to get ApolloProvider working... 7 | const { Client, Store } = context(); 8 | onData(null, { 9 | client: Client, 10 | store: Store 11 | }); 12 | }; 13 | 14 | 15 | export default composeAll( 16 | compose(onPropsChange), 17 | useDeps() 18 | )(mainLayout); 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /imports/server/main.js: -------------------------------------------------------------------------------- 1 | 2 | import {Schema} from './api/schema'; 3 | import {Resolvers} from './api/schema'; 4 | //import Connectors from './data/connectors'; 5 | import { createApolloServer } from 'meteor/apollo'; 6 | import { makeExecutableSchema } from 'graphql-tools'; 7 | 8 | 9 | 10 | const executableSchema = makeExecutableSchema({ 11 | typeDefs: Schema, 12 | resolvers: Resolvers, 13 | //connectors: Connectors, 14 | printErrors: true, // optional 15 | allowUndefinedInResolve: true // optional 16 | }); 17 | 18 | 19 | createApolloServer({ 20 | graphiql: true, 21 | pretty: true, 22 | schema: executableSchema 23 | }); 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /imports/client/modules/core/routes.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {FlowRouter} from 'meteor/kadira:flow-router'; 3 | import {mount} from 'react-mounter'; 4 | 5 | //mainLayout is the main view... 6 | import MainLayout from './containers/mainLayout'; 7 | import Home from './containers/view_home'; 8 | 9 | 10 | 11 | export default function (injectDeps) { 12 | 13 | const MainLayoutCtx = injectDeps(MainLayout); 14 | 15 | 16 | // Move these as a module and call this from a main file 17 | FlowRouter.route('/home', { 18 | name: 'home', 19 | action({}) { 20 | mount(MainLayoutCtx, { 21 | content: () => () 22 | }); 23 | } 24 | }); 25 | 26 | }; 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /imports/client/modules/core/reducers/core.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | const initialState = { 4 | //important to set defaults... 5 | toastOpen: false, 6 | message: "", 7 | someMediaImage: { name: "", cdnUrl: "" } 8 | }; 9 | 10 | 11 | export function core(state = initialState, action = {}) { 12 | //picks up a dispatched event.. and updates the Redux State accordingly... 13 | switch (action.type) { 14 | case "TOAST_SWITCH": 15 | return { 16 | ...state, 17 | toastOpen: action.toastOpen, 18 | message: action.message 19 | }; 20 | case "UPDATE_MEDIA": 21 | return { 22 | ...state, 23 | someMediaImage: action.someMediaImage 24 | }; 25 | default: 26 | return state; 27 | } 28 | } 29 | 30 | 31 | export default core; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "maps", 3 | "private": true, 4 | "scripts": { 5 | "start": "meteor run" 6 | }, 7 | "dependencies": { 8 | "apollo-client": "^0.4.12", 9 | "apollo-server": "^0.2.5", 10 | "body-parser": "^1.15.2", 11 | "express": "^4.14.0", 12 | "graphql": "^0.6.2", 13 | "graphql-tag": "^0.1.11", 14 | "graphql-tools": "^0.6.5", 15 | "lodash": "^4.15.0", 16 | "mantra-core": "^1.7.0", 17 | "material-ui": "^0.15.4", 18 | "meteor-node-stubs": "~0.2.0", 19 | "react": "^15.3.0", 20 | "react-apollo": "^0.4.7", 21 | "react-dom": "^15.3.0", 22 | "react-if": "^2.1.0", 23 | "react-komposer-plus": "^2.2.3", 24 | "react-mounter": "^1.2.0", 25 | "react-redux": "^4.4.5", 26 | "react-tap-event-plugin": "^1.0.0", 27 | "redux": "^3.5.2", 28 | "redux-logger": "^2.6.1", 29 | "redux-thunk": "^2.1.0" 30 | }, 31 | "devDependencies": { 32 | "babel-preset-meteor": "^6.12.0" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /imports/server/api/mediaGallery/schema.js: -------------------------------------------------------------------------------- 1 | import { MediaGallery } from '/imports/lib/collections'; 2 | import {check} from 'meteor/check'; 3 | 4 | 5 | export const schema = [` 6 | 7 | type MediaImage { 8 | _id: String 9 | name: String 10 | cdnUrl: String 11 | originalUrl: String 12 | size: Int 13 | uuid: String 14 | } 15 | 16 | `]; 17 | 18 | 19 | export const resolvers = { 20 | Query: { 21 | async allMediaImages(root, args) { 22 | return MediaGallery.find().fetch(); 23 | }, 24 | async mediaImage(root, args) { 25 | return MediaGallery.find().fetch(); 26 | } 27 | }, 28 | Mutation: { 29 | async addMediaImage(root, { name, cdnUrl }) { 30 | check(name, String); 31 | check(cdnUrl, String); 32 | console.log("in mutation...."); 33 | const id = MediaGallery.insert({ name: name, cdnUrl: cdnUrl }); 34 | return MediaGallery.findOne(id); 35 | } 36 | } 37 | }; -------------------------------------------------------------------------------- /imports/server/api/poi/schema.js: -------------------------------------------------------------------------------- 1 | import { Poi, MediaGallery } from '/imports/lib/collections'; 2 | import {check} from 'meteor/check'; 3 | 4 | 5 | export const schema = [` 6 | 7 | type Poi { 8 | _id: String 9 | type: String 10 | region: String 11 | title: String 12 | subtitle: String 13 | shortDescription: String 14 | description: String 15 | phone: String 16 | mail: String 17 | iconName: String 18 | lat: Float 19 | lng: Float 20 | mainImage: MediaImage 21 | } 22 | 23 | `]; 24 | 25 | 26 | export const resolvers = { 27 | Query: { 28 | async allPois(root, {}) { 29 | //get the pois 30 | return Poi.find().fetch(); 31 | }, 32 | async poiForRegion(root, {region}) { 33 | //get the pois for the provided region... 34 | return Poi.find({region: region}).fetch(); 35 | } 36 | }, 37 | Poi: { 38 | mainImage: ({mainImage}) => mainImage 39 | } 40 | }; -------------------------------------------------------------------------------- /imports/server/api/schema.js: -------------------------------------------------------------------------------- 1 | import { merge } from 'lodash'; 2 | 3 | 4 | import { schema as mediaGallerySchema, resolvers as mediaGalleryResolver } from './mediaGallery/schema'; 5 | import { schema as poiSchema, resolvers as poiResolver } from './poi/schema'; 6 | import { schema as destinationSchema, resolvers as destinationResolver } from './destination/schema'; 7 | 8 | 9 | const rootSchema = [` 10 | 11 | type Query { 12 | allMediaImages: [MediaImage] 13 | mediaImage(id: String): MediaImage 14 | allPois: [Poi] 15 | poiForRegion(region: String): [Poi] 16 | destination(slug: String): Destination 17 | } 18 | 19 | type Mutation { 20 | addMediaImage(name: String, cdnUrl: String): MediaImage 21 | } 22 | 23 | schema { 24 | query: Query 25 | mutation: Mutation 26 | } 27 | 28 | `]; 29 | 30 | export const Schema = [...rootSchema, ...mediaGallerySchema, ...poiSchema, ...destinationSchema]; 31 | export const Resolvers = merge(mediaGalleryResolver, poiResolver, destinationResolver); 32 | 33 | -------------------------------------------------------------------------------- /.meteor/packages: -------------------------------------------------------------------------------- 1 | # Meteor packages used by this project, one per line. 2 | # Check this file (and the other files in this directory) into your repository. 3 | # 4 | # 'meteor add' and 'meteor remove' will edit this file for you, 5 | # but you can also edit it by hand. 6 | 7 | meteor-base@1.0.4 # Packages every Meteor app needs to have 8 | mobile-experience@1.0.4 # Packages for a great mobile UX 9 | mongo@1.1.12 # The database Meteor supports right now 10 | blaze-html-templates@1.0.4 # Compile .html files into Meteor Blaze views 11 | reactive-var@1.0.10 # Reactive variable for tracker 12 | jquery@1.11.9 # Helpful client-side library 13 | tracker@1.1.0 # Meteor's client-side reactive programming library 14 | 15 | standard-minifier-css@1.2.0 # CSS minifier run for production mode 16 | standard-minifier-js@1.2.0 # JS minifier run for production mode 17 | es5-shim@4.6.14 # ECMAScript 5 compatibility for older browsers. 18 | ecmascript@0.5.8 # Enable ECMAScript2015+ syntax in app code 19 | 20 | shell-server@0.2.1 21 | kadira:flow-router 22 | apollo@0.1.0-beta_3 -------------------------------------------------------------------------------- /imports/server/api/destination/schema.js: -------------------------------------------------------------------------------- 1 | import { Poi, Destination } from '/imports/lib/collections'; 2 | import {check} from 'meteor/check'; 3 | 4 | 5 | export const schema = [` 6 | 7 | type GroundImage { 8 | image: String 9 | north: Float 10 | south: Float 11 | east: Float 12 | west: Float 13 | } 14 | 15 | type Destination { 16 | _id: String 17 | slug: String 18 | title: String 19 | subtitle: String 20 | mainImage: MediaImage 21 | groundImage: GroundImage 22 | pois: [Poi] 23 | } 24 | 25 | `]; 26 | 27 | 28 | export const resolvers = { 29 | Query: { 30 | async destination(root, { slug }) { 31 | console.log(slug); 32 | console.log(Destination.findOne({slug: slug})); 33 | return Destination.findOne({slug: slug}); 34 | } 35 | }, 36 | Destination: { 37 | groundImage: ({ groundImage }) => groundImage, 38 | mainImage: ({mainImage}) => mainImage, 39 | pois: ({slug}) => { 40 | console.log(slug); 41 | return Poi.find({region: slug}).fetch(); 42 | } 43 | } 44 | }; -------------------------------------------------------------------------------- /imports/client/modules/core/components/toast.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react'; 2 | import Snackbar from 'material-ui/Snackbar'; 3 | 4 | 5 | 6 | const Toast = ({toastOpen, message, closeToast}) => { 7 | 8 | const _closeToast = () => { 9 | // the => means the scope of variables belongs this component 10 | closeToast(); 11 | 12 | //this call the action 13 | //which then dispatches an event.. 14 | //which is picked up by the reducer, which updates the state. 15 | //state is passed as a prop (toastOpen / message) from the container.. 16 | //phew... 17 | }; 18 | 19 | 20 | return ( 21 |
22 | 28 |
29 | ); 30 | }; 31 | 32 | 33 | 34 | Toast.propTypes = { 35 | allPois: React.PropTypes.array, 36 | hasErrors: React.PropTypes.bool, 37 | loading: React.PropTypes.bool, 38 | toastOpen: React.PropTypes.bool, 39 | message: PropTypes.string, 40 | closeToast: PropTypes.func 41 | }; 42 | 43 | 44 | export default Toast; 45 | -------------------------------------------------------------------------------- /imports/client/configs/context.js: -------------------------------------------------------------------------------- 1 | import * as Collections from '/imports/lib/collections'; 2 | import {Meteor} from 'meteor/meteor'; 3 | import { meteorClientConfig } from 'meteor/apollo'; 4 | import ApolloClient from 'apollo-client'; 5 | import {FlowRouter} from 'meteor/kadira:flow-router'; 6 | import {ReactiveDict} from 'meteor/reactive-dict'; 7 | import {Tracker} from 'meteor/tracker'; 8 | 9 | 10 | //redux stuff 11 | import { createStore, applyMiddleware, combineReducers, compose } from 'redux'; 12 | import createLogger from 'redux-logger' 13 | import ReduxThunk from 'redux-thunk' 14 | 15 | 16 | export function initContext({ reducers }) { 17 | 18 | //powered by the Meteor Atmosphere Package.. "meteorClientConfig" 19 | const Client = new ApolloClient(meteorClientConfig()); 20 | 21 | const reducer = combineReducers({ 22 | ...reducers, 23 | apollo: Client.reducer() 24 | }); 25 | 26 | 27 | // put all Redux middleware here 28 | const middleware = [ 29 | createLogger(), 30 | Client.middleware(), 31 | ReduxThunk 32 | ]; 33 | 34 | 35 | let Store = createStore(reducer, {}, compose( 36 | applyMiddleware(...middleware), 37 | window.devToolsExtension ? window.devToolsExtension() : f => f 38 | )); 39 | 40 | 41 | return { 42 | Meteor, 43 | FlowRouter, 44 | Collections, 45 | Tracker, 46 | Client, 47 | Store, 48 | dispatch: Store.dispatch 49 | }; 50 | } 51 | -------------------------------------------------------------------------------- /imports/client/modules/core/containers/view_home.js: -------------------------------------------------------------------------------- 1 | import {useDeps} from 'mantra-core'; 2 | import {withRedux, composeAll} from 'react-komposer-plus'; 3 | import { graphql } from 'react-apollo'; 4 | import gql from 'graphql-tag'; 5 | import view_home from '../components/view_home'; 6 | 7 | 8 | 9 | const ADD_MEDIA_IMAGE = gql` 10 | mutation addMediaImageMutation ($name: String!, $cdnUrl: String!){ 11 | addMediaImage( 12 | name: $name, cdnUrl: $cdnUrl) 13 | { 14 | _id 15 | name 16 | cdnUrl 17 | } 18 | }`; 19 | 20 | 21 | const WITH_ADD_MEDIA_IMAGE = graphql(ADD_MEDIA_IMAGE, { 22 | //we are returning the mutation as a prop. 23 | //this will then be passed to the action 24 | props: ({ ownProps, mutate }) => ({ 25 | addMediaImageMutation(variables ) { 26 | return mutate({ 27 | variables: {...variables} 28 | }); 29 | } 30 | }) 31 | }); 32 | 33 | 34 | const mapStateToProps = ({ core }) => ({ 35 | someMediaImage: core.someMediaImage 36 | }); 37 | 38 | 39 | export const depsMapper = (context, actions) => ({ 40 | openToast: actions.toast.openToast, 41 | addMediaImage: actions.toast.addMediaImage, 42 | context: () => context 43 | }); 44 | 45 | 46 | //interesting to note.. the order of these matter.. withRedux before useDeps 47 | export default composeAll( 48 | withRedux(mapStateToProps), 49 | useDeps(depsMapper), 50 | WITH_ADD_MEDIA_IMAGE 51 | )(view_home); -------------------------------------------------------------------------------- /imports/client/modules/core/actions/toast.js: -------------------------------------------------------------------------------- 1 | export default { 2 | openToast({Meteor, Store}) { 3 | console.log('dispatching toast...'); 4 | //this will be picked up by the reducer... 5 | return Store.dispatch({ 6 | type: 'TOAST_SWITCH', 7 | toastOpen: true, 8 | message: 'I am an over complex TOAST instance...' 9 | }); 10 | }, 11 | closeToast({Meteor, Store}) { 12 | //this will be picked up by the reducer... 13 | return Store.dispatch({ 14 | type: 'TOAST_SWITCH', 15 | toastOpen: false, 16 | message: 'Toast begone....' 17 | }); 18 | }, 19 | addMediaImage({}, mutation, variables) { 20 | return (dispatch, getState) => { 21 | //not sure if this is 100% correct.. but it works. 22 | //this is Thunk in action 23 | //we wait for the result.. and then auto dispatch to a listening reducer to update the state 24 | 25 | mutation(variables).then((result) => { 26 | if (result.data) { 27 | console.log(result); 28 | // if the mutation yields data, dispatch an action with that data 29 | return dispatch ({ 30 | type: "UPDATE_MEDIA", 31 | someMediaImage: result.data.addMediaImage //the mutation returns this object.. 32 | }); 33 | } 34 | }); 35 | }; 36 | } 37 | }; -------------------------------------------------------------------------------- /imports/client/modules/core/components/mainLayout.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react'; 2 | import { ApolloProvider } from 'react-apollo'; 3 | import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider'; 4 | import lightBaseTheme from 'material-ui/styles/baseThemes/lightBaseTheme'; 5 | import getMuiTheme from 'material-ui/styles/getMuiTheme' 6 | 7 | 8 | const lightMuiTheme = getMuiTheme(lightBaseTheme); 9 | 10 | /** 11 | * A lot of interesting stuff happening here to get MUI and Redux 12 | * working with the app with the help of Providers below 13 | */ 14 | 15 | const propTypes = { 16 | nav: PropTypes.func, 17 | content: PropTypes.func.isRequired, 18 | footer: PropTypes.func, 19 | client: PropTypes.object, 20 | store: PropTypes.object 21 | }; 22 | 23 | 24 | const Layout = ({ 25 | content = () => null, 26 | nav = () => null, 27 | footer = () => null, 28 | client, 29 | store 30 | }) => ( 31 | 32 | 33 |
34 |
35 | {nav()} 36 |
37 | 38 |
39 | {content()} 40 |
41 | 42 |
43 |
44 |
45 |
46 |
47 | ); 48 | 49 | Layout.propTypes = propTypes; 50 | Layout.displayName = "Main Layout"; 51 | 52 | export default Layout; 53 | -------------------------------------------------------------------------------- /imports/client/modules/core/containers/toast.js: -------------------------------------------------------------------------------- 1 | import {useDeps} from 'mantra-core'; 2 | import {withRedux, composeAll} from 'react-komposer-plus'; 3 | import { graphql } from 'react-apollo'; 4 | import gql from 'graphql-tag'; 5 | import toast from '../components/toast'; 6 | 7 | 8 | /** 9 | * 10 | * Notice the react-komposer-plus package.. this has some handy Redux stuff. 11 | * Which means we stay away from using Redux Connect() in the ComposeAll function 12 | * 13 | * We use the new GraphQL connector to handle the main query 14 | * http://docs.apollostack.com/apollo-client/react.html 15 | */ 16 | 17 | 18 | 19 | const GET_POIS = gql` 20 | query allPois { 21 | allPois { 22 | _id 23 | type 24 | region 25 | } 26 | }`; 27 | 28 | //we can define the exact props we want.. this is a good habit 29 | //instead of just dumping a data object to the component 30 | const GET_POIS_WITH_PROPS = graphql(GET_POIS, { 31 | props: ({ ownProps, data }) => { 32 | if (data.loading) return { loading: true }; 33 | if (data.error) return { hasErrors: true }; 34 | return { 35 | allPois: data.allPois 36 | }; 37 | } 38 | }); 39 | 40 | 41 | 42 | const mapStateToProps = ({ core }) => ({ 43 | toastOpen: core.toastOpen, 44 | message: core.message 45 | }); 46 | 47 | 48 | //sending te actions and context to the component 49 | const mapDepsToProps = (context, actions) => ({ 50 | closeToast: actions.toast.closeToast, 51 | context: () => context 52 | }); 53 | 54 | 55 | 56 | export default composeAll( 57 | withRedux(mapStateToProps), 58 | useDeps(mapDepsToProps), 59 | GET_POIS_WITH_PROPS 60 | )(toast); 61 | 62 | -------------------------------------------------------------------------------- /.meteor/versions: -------------------------------------------------------------------------------- 1 | accounts-base@1.2.11 2 | allow-deny@1.0.5 3 | apollo@0.1.0-beta_3 4 | autoupdate@1.3.11 5 | babel-compiler@6.9.1 6 | babel-runtime@0.1.11 7 | base64@1.0.9 8 | binary-heap@1.0.9 9 | blaze@2.1.8 10 | blaze-html-templates@1.0.4 11 | blaze-tools@1.0.9 12 | boilerplate-generator@1.0.9 13 | caching-compiler@1.1.7 14 | caching-html-compiler@1.0.6 15 | callback-hook@1.0.9 16 | check@1.2.3 17 | ddp@1.2.5 18 | ddp-client@1.3.1 19 | ddp-common@1.2.6 20 | ddp-rate-limiter@1.0.5 21 | ddp-server@1.3.10 22 | deps@1.0.12 23 | diff-sequence@1.0.6 24 | ecmascript@0.5.8 25 | ecmascript-runtime@0.3.14 26 | ejson@1.0.12 27 | es5-shim@4.6.14 28 | fastclick@1.0.12 29 | geojson-utils@1.0.9 30 | hot-code-push@1.0.4 31 | html-tools@1.0.10 32 | htmljs@1.0.10 33 | http@1.2.9 34 | id-map@1.0.8 35 | jquery@1.11.9 36 | kadira:flow-router@2.12.1 37 | launch-screen@1.0.12 38 | livedata@1.0.18 39 | localstorage@1.0.11 40 | logging@1.1.15 41 | meteor@1.2.17 42 | meteor-base@1.0.4 43 | minifier-css@1.2.14 44 | minifier-js@1.2.14 45 | minimongo@1.0.17 46 | mobile-experience@1.0.4 47 | mobile-status-bar@1.0.12 48 | modules@0.7.6 49 | modules-runtime@0.7.6 50 | mongo@1.1.12 51 | mongo-id@1.0.5 52 | npm-mongo@1.5.48 53 | observe-sequence@1.0.12 54 | ordered-dict@1.0.8 55 | promise@0.8.4 56 | random@1.0.10 57 | rate-limit@1.0.5 58 | reactive-dict@1.1.8 59 | reactive-var@1.0.10 60 | reload@1.1.10 61 | retry@1.0.8 62 | routepolicy@1.0.11 63 | service-configuration@1.0.10 64 | shell-server@0.2.1 65 | spacebars@1.0.12 66 | spacebars-compiler@1.0.12 67 | standard-minifier-css@1.2.0 68 | standard-minifier-js@1.2.0 69 | templating@1.2.14 70 | templating-tools@1.0.4 71 | tmeasday:check-npm-versions@0.3.1 72 | tracker@1.1.0 73 | ui@1.0.11 74 | underscore@1.0.9 75 | url@1.0.10 76 | webapp@1.3.11 77 | webapp-hashing@1.0.9 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Apollo, Redux, Mantra and Meteor 2 | 3 | 4 | ## An overcomplex attempt to trigger some Toast 5 | 6 | 7 | [Snackbars and Toast](https://material.google.com/components/snackbars-toasts.html) are nice simple UI experiences. The Material UI Snackbar is simple enough to manage via State. 8 | 9 | ``` 10 | 16 | ``` 17 | 18 | However.. it's important to geek out. Today we have new opportunities to overcomplicate even the simplest of features. So let's wire up some Toast to a Redux State.. managed by Apollo.. and use Mantra to organize the codebase... on Meteor. Just because ;) 19 | 20 | 21 | ### Modules 22 | 23 | There is a single module here.. 24 | 25 | * **core**: Which is where the Toast is managed. You will find some basic redux state to switch the toast on / off. There is also a mutation to add a MediaImage object to a collection. 26 | 27 | 28 | ### GraphQL Schema 29 | 30 | Check out the Schema set up on the server.. A lot of the schema is not used in this simple demo app. But there is a clear example of splitting down a schema into separate files. 31 | 32 | 33 | 34 | 35 | ### What happened to ReduxToast v1? 36 | 37 | Great question.. it was a work of art. However, the recent updates to react-apollo introduced the new GraphQL container, which put a spanner in the works to pass props / state and actions to a component. 38 | 39 | This latest Toast adventure manages to get everything working again (for now). 40 | 41 | 42 | ### Obvious Disclaimer 43 | 44 | 45 | This is basic striped down code.. to showcase how I got the various parts working together. Still work in progress, and it's safe to assume a few improvements are still to come. 46 | 47 | The whole redux / mantra / apollo mix has been a very confusing learning curve on Meteor. And it's still a maturing stack 48 | 49 | I think the idea here is to let Redux handle the local state / domain and send all props to components via Connect(). Not really leveraging MinMongo on the client. Redux / apollo provides some nice features to manage the fetching / caching / updating of the State. This feature set is going to improve moving forward with Apollo. 50 | 51 | 52 | If you find this helpful... please follow me. 53 | 54 | [@gavin_payne](https://twitter.com/gavin_payne) 55 | 56 | -------------------------------------------------------------------------------- /imports/client/modules/core/components/view_home.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react'; 2 | import RaisedButton from 'material-ui/RaisedButton'; 3 | import Toast from '../containers/toast'; 4 | import {Card, CardMedia, CardTitle} from 'material-ui/Card'; 5 | import { If, Then, Else } from 'react-if'; 6 | 7 | 8 | 9 | const View_Home = ({openToast, addMediaImageMutation, addMediaImage, context, someMediaImage}) => { 10 | 11 | //I do love functions in ES6 12 | const _goToast = () => { 13 | // the => means the scope of variables belongs to this component 14 | openToast(); //calls the Action. 15 | }; 16 | 17 | 18 | const _goMutate = () => { 19 | 20 | //dispatch is in the Mantra context.. 21 | const { dispatch } = context(); 22 | 23 | //im gonna mutate... 24 | //looks like we have to pass the mutation to the action here... 25 | //this function then uses the Thunk middleware..(i think) 26 | 27 | const variables = { 28 | name: "I am the MUTATION....", 29 | cdnUrl: "https://img.ifcdn.com/images/d1e8e77fe0ff800ec3e84c701d0145aa10824ad8f558cd543f25421dbcdbc9c0_1.jpg" 30 | }; 31 | 32 | 33 | //we don't call the function here.. just pass it plus variables to the action 34 | //we do however dispatch.. this triggers the thunk 35 | return dispatch(addMediaImage(addMediaImageMutation, variables)); 36 | }; 37 | 38 | return ( 39 |
40 | 44 | 45 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | {() => // will only be evaluated if the condition fails. 61 |
Waiting for MUTATION to happen...
62 | }
63 |
64 | 65 | 66 |
67 | ); 68 | 69 | }; 70 | 71 | View_Home.propTypes = { 72 | openToast: React.PropTypes.func, //action 73 | addMediaImageMutation: React.PropTypes.func, //the mutation 74 | addMediaImage: React.PropTypes.func, //action 75 | someMediaImage: React.PropTypes.object //state 76 | }; 77 | 78 | 79 | View_Home.displayName = "View Home"; 80 | export default View_Home; --------------------------------------------------------------------------------