├── functions ├── .keep └── hello.js ├── app ├── css │ ├── common.less │ ├── images │ │ ├── timg.jpg │ │ └── user-bg.jpg │ └── resets.less ├── components │ ├── Explore │ │ ├── style.less │ │ └── index.js │ ├── List │ │ └── index.js │ └── LoadMore │ │ └── index.js ├── containers │ ├── UserPage │ │ ├── style.less │ │ ├── action.js │ │ ├── reducer.js │ │ └── index.js │ ├── RepoPage │ │ └── index.js │ └── App │ │ ├── constant.js │ │ ├── action.js │ │ ├── reducer.js │ │ ├── style.less │ │ └── index.js ├── config │ ├── reducer.js │ ├── env.js │ ├── store.js │ └── route.js ├── app.js ├── utils │ ├── assign.js │ ├── request.js │ ├── pager.js │ ├── wptr.1.1.js │ └── api.js └── middleware │ └── api.js ├── favicon.ico ├── .editorconfig ├── .babelrc ├── server.js ├── LICENSE ├── index.html ├── package.json └── README.md /functions/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/css/common.less: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/components/Explore/style.less: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/containers/UserPage/style.less: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Leotw/floor-produce/HEAD/favicon.ico -------------------------------------------------------------------------------- /app/containers/RepoPage/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by tiwenleo on 17/10/6. 3 | */ 4 | -------------------------------------------------------------------------------- /app/css/images/timg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Leotw/floor-produce/HEAD/app/css/images/timg.jpg -------------------------------------------------------------------------------- /app/css/images/user-bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Leotw/floor-produce/HEAD/app/css/images/user-bg.jpg -------------------------------------------------------------------------------- /functions/hello.js: -------------------------------------------------------------------------------- 1 | export function onRequest(context) { 2 | return new Response("Hello, world!") 3 | } 4 | -------------------------------------------------------------------------------- /app/containers/App/constant.js: -------------------------------------------------------------------------------- 1 | export const TRANSITION_LEFT = 'TRANSITION_LEFT' 2 | 3 | export const TRANSITION_RIGHT = 'TRANSITION_RIGHT' 4 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true; 2 | 3 | [*] 4 | trim_trailing_whitespace = true 5 | insert_final_newline = true 6 | indent_style = space 7 | indent_size = 2 8 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["latest", { 4 | "es2015": { 5 | "loose": true 6 | } 7 | }], 8 | "react", 9 | "stage-0" 10 | ], 11 | "plugins": [ 12 | ["import", { 13 | "style": "css", 14 | "libraryName": "antd-mobile" 15 | } 16 | ] 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /app/containers/App/action.js: -------------------------------------------------------------------------------- 1 | 2 | import { 3 | TRANSITION_LEFT, 4 | TRANSITION_RIGHT, 5 | } from './constant' 6 | 7 | export function transLeft() { 8 | return { 9 | type: TRANSITION_LEFT, 10 | } 11 | } 12 | 13 | export function transRight() { 14 | console.log('transRight'); 15 | return { 16 | type: TRANSITION_RIGHT, 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var port = 9601; 3 | var app = express(); 4 | 5 | app.use(express.static(__dirname + '/dist')); 6 | 7 | console.log('[DIR]:::' + __dirname + '/dist'); 8 | 9 | module.exports = app.listen(port, function (err) { 10 | if (err) { 11 | console.log(err) 12 | return 13 | } 14 | console.log('[LOG]:::Listening at http://localhost:' + port + '\n') 15 | }) 16 | -------------------------------------------------------------------------------- /app/config/reducer.js: -------------------------------------------------------------------------------- 1 | import { combineReducers } from "redux"; 2 | import { routerReducer } from "react-router-redux"; 3 | import { users, errorMessageOfUsers, fetchOfUser, starredRepo, fetchOfRepo } from "../containers/UserPage/reducer"; 4 | 5 | const rootReducer = combineReducers({ 6 | users, 7 | errorMessageOfUsers, 8 | fetchOfUser, 9 | starredRepo, 10 | fetchOfRepo, 11 | routing: routerReducer 12 | }); 13 | 14 | export default rootReducer 15 | -------------------------------------------------------------------------------- /app/components/List/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by tiwenleo on 17/10/6. 3 | */ 4 | import React, { Component } from "react"; 5 | const List = ({items}) => { 6 | items = items || []; 7 | return ( 8 |
9 | {items.map((e)=> { 10 | return
11 | {e.name}by 12 | {e.owner.login} 13 |

{e.description}

14 |
15 | })} 16 |
); 17 | }; 18 | export default List; 19 | -------------------------------------------------------------------------------- /app/app.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import { Provider } from 'react-redux'; 4 | import './css/resets.less'; 5 | import route from './config/route'; 6 | import configureStore from './config/store'; 7 | 8 | 9 | const store = configureStore() 10 | // 添加Global订阅事件 11 | store.subscribe(function () { 12 | // console.log('subscribe...::::....' + store.getState()); 13 | }); 14 | 15 | ReactDOM.render( 16 | {route}, 17 | document.getElementById('app') 18 | ); 19 | -------------------------------------------------------------------------------- /app/config/env.js: -------------------------------------------------------------------------------- 1 | // http server host 2 | const server = { 3 | mock : 'mock;http://localhost:7709', 4 | stage : 'stage;https://api.github.com/', 5 | prod : 'prod;https://xxx.xxxxx.com.cn', 6 | proxy : 'proxy;', // webpack http-proxy-middleware 7 | native: 'native;', 8 | } 9 | 10 | /////////////////////////////////////////////// 11 | // 选择调试server 12 | // export const env = server.mock; 13 | export const env = server.stage; 14 | // export const env = server.prod; 15 | // export const env = server.proxy; 16 | // export const env = server.native; 17 | /////////////////////////////////////////////// 18 | 19 | // add token 20 | export const token = '4c9fa540de3347df9ddb91907114fa57'; 21 | export const zoneCode = '0575'; 22 | -------------------------------------------------------------------------------- /app/containers/App/reducer.js: -------------------------------------------------------------------------------- 1 | /************************************************/ 2 | /********** Reducer 控制State——业务逻辑 ***********/ 3 | /************************************************/ 4 | import { 5 | TRANSITION_LEFT, 6 | TRANSITION_RIGHT 7 | } from './constant'; 8 | 9 | const initialState = { 10 | transName: 'right' 11 | }; // 可以是Number 或者字符串 或对象 12 | 13 | const transReducer = (state = initialState, action) => { 14 | switch (action.type) { 15 | case TRANSITION_LEFT: 16 | return Object.assign({}, state, { 17 | transName: 'left' 18 | }) 19 | case TRANSITION_RIGHT: 20 | return Object.assign({}, state, { 21 | transName: 'right' 22 | }) 23 | default: 24 | return state; 25 | } 26 | }; 27 | 28 | export default transReducer; 29 | -------------------------------------------------------------------------------- /app/containers/UserPage/action.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by tiwenleo on 17/10/6. 3 | */ 4 | import loginApi from "../../utils/api"; 5 | 6 | const USER_REQUEST_STARTED = 'USER_REQUEST_STARTED'; 7 | const LOAD_USER = 'LOAD_USER'; 8 | 9 | const userRequest = (login) => ({ 10 | ['callFetch']: { 11 | types: [USER_REQUEST_STARTED, LOAD_USER], 12 | url: `${loginApi}${login}`, 13 | login 14 | } 15 | }); 16 | 17 | const STARRED_REQUEST_STARTED = 'STARRED_REQUEST_STARTED'; 18 | const LOAD_STARRED_REPO = 'LOAD_STARRED_REPO'; 19 | 20 | const starredRepoRequest = (login) => ({ 21 | ['callFetch']: { 22 | types: [STARRED_REQUEST_STARTED, LOAD_STARRED_REPO], 23 | url: `${loginApi}${login}`, 24 | login 25 | } 26 | }); 27 | 28 | 29 | export { userRequest, starredRepoRequest }; 30 | -------------------------------------------------------------------------------- /app/components/LoadMore/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by tiwenleo on 17/10/7. 3 | */ 4 | import React, { Component } from "react"; 5 | 6 | const LoadMore = ({fetch, callback}) => { 7 | setTimeout(()=> { 8 | let loadMoreWrapper = document.getElementById('loadMoreWrapper'); 9 | window.addEventListener('scroll', function() { 10 | if (fetch) { 11 | return false; 12 | } 13 | let top = loadMoreWrapper.getBoundingClientRect().top; 14 | let windowHeight = window.screen.height; 15 | if (top && top < windowHeight) { 16 | callback(); 17 | } 18 | }, false); 19 | }, 500); 20 | return ( 21 |
22 | {fetch ?

正在加载

:

加载更多

} 23 |
24 | ) 25 | }; 26 | export default LoadMore; 27 | -------------------------------------------------------------------------------- /app/config/store.js: -------------------------------------------------------------------------------- 1 | import { createStore, combineReducers, applyMiddleware } from "redux"; 2 | import thunkMiddleware from "redux-thunk"; 3 | import { createLogger } from "redux-logger"; 4 | import rootReducer from "./reducer"; 5 | import api from "../middleware/api"; 6 | 7 | const createStoreWithMiddleware = applyMiddleware( 8 | thunkMiddleware, 9 | api, 10 | createLogger(), 11 | )(createStore); 12 | 13 | export default function configureStore(initialState) { 14 | const store = createStoreWithMiddleware(rootReducer, initialState) 15 | 16 | if (module.hot) { 17 | // Enable Webpack hot module replacement for reducers 18 | module.hot.accept('./reducer', () => { 19 | const nextRootReducer = require('./reducer') 20 | store.replaceReducer(nextRootReducer) 21 | }) 22 | } 23 | 24 | return store 25 | } 26 | -------------------------------------------------------------------------------- /app/components/Explore/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by tiwenleo on 17/10/6. 3 | */ 4 | import React, { Component } from "react"; 5 | const GITHUB_REPO = 'https://github.com/Leotw'; 6 | const Explore = ({getLogin, value}) => { 7 | let input; 8 | const setLogin = () => { 9 | getLogin(input.value); 10 | }; 11 | return ( 12 |
13 |

Type a username or repo full name and hit 'Go':

14 | { 18 | input = node 19 | }}/> 20 | 21 |

22 | Code on Github. 23 |

24 |

25 | Move the DevTools with Ctrl+W or hide them with Ctrl+H. 26 |

27 |
28 | ); 29 | }; 30 | export default Explore; 31 | -------------------------------------------------------------------------------- /app/config/route.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { Router, Route, IndexRoute, browserHistory, hashHistory, applyRouterMiddleware } from "react-router"; 3 | import { syncHistoryWithStore } from "react-router-redux"; 4 | import { useScroll } from "react-router-scroll"; 5 | import configureStore from "./store"; 6 | import App from "../containers/App"; 7 | import UserPage from "../containers/UserPage"; 8 | import RepoPage from "../containers/RepoPage"; 9 | 10 | const history = syncHistoryWithStore(browserHistory, configureStore()); 11 | 12 | const config = [ 13 | { 14 | path: '/', 15 | component: App, 16 | // default index 17 | childRoutes: [ 18 | {path: '/:login', name: 'UserPage', component: UserPage}, 19 | {path: '/:login/:name', name: 'RepoPage', component: RepoPage}, 20 | ] 21 | } 22 | ] 23 | 24 | const route = ( 25 | 29 | 30 | ) 31 | 32 | 33 | export default route 34 | -------------------------------------------------------------------------------- /app/containers/App/style.less: -------------------------------------------------------------------------------- 1 | .transition-wrapper { 2 | position: relative; 3 | } 4 | /* 翻页 */ 5 | .left-enter, .right-enter { 6 | opacity: 0.01; 7 | } 8 | 9 | .left-enter.left-enter-active { 10 | opacity: 1; 11 | animation: left-slide 200ms linear; 12 | transition: opacity 150ms ease-in; 13 | } 14 | 15 | .left-leave { 16 | opacity: 1; 17 | } 18 | 19 | .left-leave.left-leave-active, .right-leave.right-leave-active { 20 | opacity: 0.01; 21 | transition: opacity 100ms ease-in; 22 | } 23 | 24 | @keyframes left-slide { 25 | from { 26 | opacity: 0.01; 27 | transform:translateX(100%); 28 | } 29 | to { 30 | opacity: 1; 31 | transform:translateX(0%); 32 | } 33 | } 34 | 35 | .right-enter.right-enter-active { 36 | opacity: 1; 37 | animation: rightSlide 200ms linear; 38 | transition: opacity 150ms ease-in; 39 | } 40 | 41 | .right-leave { 42 | opacity: 1; 43 | } 44 | 45 | @keyframes rightSlide { 46 | from { 47 | opacity: 0.01; 48 | transform:translateX(-100%); 49 | } 50 | to { 51 | opacity: 1; 52 | transform:translateX(0%); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 天空 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /app/utils/assign.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by tiwenleo on 17/10/7. 3 | */ 4 | if (!Object.assign) { 5 | // 定义assign方法 6 | Object.defineProperty(Object, 'assign', { 7 | enumerable: false, 8 | configurable: true, 9 | writable: true, 10 | value: function(target) { // assign方法的第一个参数 11 | 'use strict'; 12 | // 第一个参数为空,则抛错 13 | if (target === undefined || target === null) { 14 | throw new TypeError('Cannot convert first argument to object'); 15 | } 16 | 17 | var to = Object(target); 18 | // 遍历剩余所有参数 19 | for (var i = 1; i < arguments.length; i++) { 20 | var nextSource = arguments[i]; 21 | // 参数为空,则跳过,继续下一个 22 | if (nextSource === undefined || nextSource === null) { 23 | continue; 24 | } 25 | nextSource = Object(nextSource); 26 | 27 | // 获取改参数的所有key值,并遍历 28 | var keysArray = Object.keys(nextSource); 29 | for (var nextIndex = 0, len = keysArray.length; nextIndex < len; nextIndex++) { 30 | var nextKey = keysArray[nextIndex]; 31 | var desc = Object.getOwnPropertyDescriptor(nextSource, nextKey); 32 | // 如果不为空且可枚举,则直接浅拷贝赋值 33 | if (desc !== undefined && desc.enumerable) { 34 | to[nextKey] = nextSource[nextKey]; 35 | } 36 | } 37 | } 38 | return to; 39 | } 40 | }); 41 | } 42 | -------------------------------------------------------------------------------- /app/containers/UserPage/reducer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by tiwenleo on 17/10/6. 3 | */ 4 | import '../../utils/assign'; 5 | 6 | export const users = (state = {}, action) => { 7 | if (action.type === 'LOAD_USER' && action.response) { 8 | return Object.assign({}, state, action.response); 9 | } else { 10 | return state; 11 | } 12 | }; 13 | 14 | export const starredRepo = (state = { 15 | pagination: {}, 16 | repos: [] 17 | }, action) => { 18 | if (action.type === 'LOAD_STARRED_REPO') { 19 | return Object.assign({}, state, action.response); 20 | } else { 21 | return state; 22 | } 23 | }; 24 | 25 | export const errorMessageOfUsers = (state = null, action) => { 26 | if ((action.type === 'LOAD_USER' || action.type === 'LOAD_STARRED_REPO') && action.error) { 27 | return action.error; 28 | } else { 29 | return null; 30 | } 31 | }; 32 | 33 | export const fetchOfUser = (state = false, action) => { 34 | if (action.type === 'USER_REQUEST_STARTED') { 35 | return true; 36 | } else if (action.type === 'LOAD_USER') { 37 | return false; 38 | } else { 39 | return state; 40 | } 41 | }; 42 | 43 | export const fetchOfRepo = (state = false, action) => { 44 | if (action.type === 'STARRED_REQUEST_STARTED') { 45 | return true; 46 | } else if (action.type === 'LOAD_STARRED_REPO') { 47 | return false; 48 | } else { 49 | return state; 50 | } 51 | }; 52 | -------------------------------------------------------------------------------- /app/css/resets.less: -------------------------------------------------------------------------------- 1 | html, 2 | body, 3 | div, 4 | ul, 5 | li, 6 | p, 7 | footer, 8 | header, 9 | article, 10 | a, 11 | b, 12 | em, 13 | nav, 14 | form, 15 | input, 16 | textarea, 17 | select, 18 | button, 19 | i, 20 | h1, 21 | h2, 22 | h3, 23 | h4, 24 | h5, 25 | h6 { 26 | padding: 0; 27 | margin: 0; 28 | border: none; 29 | list-style: none; 30 | text-decoration: none; 31 | box-sizing: border-box; 32 | } 33 | 34 | input, 35 | textarea, 36 | button { 37 | &:focus { 38 | outline: none; 39 | } 40 | } 41 | 42 | body { 43 | font: 0.14rem/1.5 "Helvetica Neue", Helvetica, Arial, "Microsoft Yahei", "Hiragino Sans GB", "Heiti SC", "WenQuanYi Micro Hei", sans-serif; 44 | color: #040404; 45 | background: #eee; 46 | } 47 | 48 | html, 49 | body { 50 | width: 100%; 51 | height: 100%; 52 | -webkit-text-size-adjust: 100%; 53 | 54 | } 55 | 56 | header, 57 | nav, 58 | footer, 59 | article { 60 | display: block; 61 | } 62 | 63 | a { 64 | background-color: transparent; 65 | -webkit-text-decoration-skip: objects; 66 | } 67 | 68 | a:active, 69 | a:hover { 70 | outline-width: 0; 71 | } 72 | 73 | ::-webkit-input-placeholder { 74 | color: inherit; 75 | opacity: 0.54; 76 | } 77 | 78 | ::-webkit-file-upload-button { 79 | -webkit-appearance: button; 80 | font: inherit; 81 | } 82 | 83 | .scrolling { 84 | overflow-y: scroll!important; 85 | -webkit-overflow-scrolling: touch!important; 86 | } 87 | 88 | img { 89 | border: none; 90 | max-width: 100%; 91 | } 92 | -------------------------------------------------------------------------------- /app/containers/App/index.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { connect } from "react-redux"; 3 | import { browserHistory } from "react-router"; 4 | import Explore from "../../components/Explore"; 5 | import ReactCSSTransitionGroup from "react-addons-css-transition-group"; 6 | import "./style.less"; 7 | import "../../css/common.less"; 8 | 9 | class App extends Component { 10 | 11 | constructor(props) { 12 | super(props) 13 | } 14 | 15 | getLogin = (value) => { 16 | browserHistory.push(value); 17 | 18 | }; 19 | 20 | render() { 21 | const {fetchOfUser, login} = this.props; 22 | return ( 23 | 29 | 30 |
33 | 34 | {fetchOfUser ?

Loading

: null} 35 |
{this.props.children}
36 |
37 | 38 |
39 | ) 40 | } 41 | } 42 | 43 | const mapStateToProps = (state, ownProps) => ({ 44 | fetchOfUser: state.fetchOfUser, 45 | login: ownProps.params.login ? ownProps.params.login.toLowerCase() : '' 46 | }); 47 | 48 | export default connect(mapStateToProps, {})(App) 49 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | react-orcrist 11 | 15 | 16 | 17 | 18 | 19 | 26 |
27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /app/containers/UserPage/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by tiwenleo on 17/10/6. 3 | */ 4 | import React, { Component, PropTypes } from "react"; 5 | import { userRequest, starredRepoRequest } from "./action"; 6 | import List from "../../components/List"; 7 | import LoadMore from "../../components/LoadMore"; 8 | import { connect } from "react-redux"; 9 | 10 | class UserPage extends Component { 11 | constructor(props) { 12 | super(props); 13 | } 14 | 15 | componentDidMount() { 16 | const {userRequest, login, starredRepoRequest, fetchOfUser, fetchOfRepo} = this.props; 17 | if (fetchOfUser || fetchOfRepo) { 18 | return false; 19 | } 20 | userRequest(login); 21 | starredRepoRequest(login); 22 | } 23 | 24 | loadMoreStarredRepo = () => { 25 | const {login, starredRepoRequest} = this.props; 26 | starredRepoRequest(login); 27 | }; 28 | 29 | render() { 30 | const {users, starredRepo, errorMessageOfUsers, fetchOfUser, fetchOfRepo} = this.props; 31 | 32 | let name = users.name ? `(${users.name})` : ''; 33 | return ( 34 |
35 | avatar 36 | {fetchOfUser ?

loading...

:

{users.login}{name}

} 37 | {errorMessageOfUsers} 38 | 39 | 42 |
43 | ); 44 | } 45 | } 46 | const mapStateToProps = (state, ownProps) => { 47 | const login = ownProps.params.login.toLowerCase(); 48 | const {users, starredRepo, errorMessageOfUsers, fetchOfUser, fetchOfRepo} = state; 49 | return { 50 | login, 51 | users, 52 | starredRepo, 53 | errorMessageOfUsers, 54 | fetchOfUser, 55 | fetchOfRepo 56 | } 57 | }; 58 | 59 | export default connect( 60 | mapStateToProps, 61 | {userRequest, starredRepoRequest} 62 | )(UserPage); 63 | -------------------------------------------------------------------------------- /app/middleware/api.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by tiwenleo on 17/10/7. 3 | */ 4 | import request from "../utils/request"; 5 | 6 | const getNextPage = (getState, login) => { 7 | return getState().starredRepo.pagination[login] || 1 8 | }; 9 | 10 | const getRepos = (getState) => { 11 | return getState().starredRepo.repos || []; 12 | }; 13 | 14 | const getFetchData = (opt) => { 15 | const {next, login, nextPage, repos, requestSuccess, url, callback} = opt; 16 | callback && callback(); 17 | let tmpObj = null; 18 | request('get', url).promise 19 | .then(res => { 20 | Object.prototype.toString.call(res) === '[object Array]' ? res = res || [] : res = res || {}; 21 | requestSuccess === 'LOAD_STARRED_REPO' ? tmpObj = { 22 | pagination: { 23 | [login]: nextPage + 1, 24 | }, 25 | repos: [...repos, ...res] 26 | } : tmpObj = res; 27 | return next({ 28 | type: requestSuccess, 29 | response: tmpObj 30 | }); 31 | }) 32 | .catch(error => { 33 | return next({ 34 | type: requestSuccess, 35 | error: typeof error === 'object' ? JSON.stringify(error) : error 36 | }); 37 | }); 38 | }; 39 | 40 | export default ({getState, dispatch}) => (next) => (action) => { 41 | console.log('This is from middleware api::'); 42 | const callFetch = action['callFetch']; 43 | if (!callFetch) { 44 | return next(action); 45 | } 46 | const {types, url, login} = callFetch; 47 | const [requestStarted,requestSuccess] = types; 48 | if (requestStarted === 'USER_REQUEST_STARTED') { 49 | next({type: requestStarted}); 50 | getFetchData({next, login, requestSuccess, url}); 51 | } else if (requestStarted === 'STARRED_REQUEST_STARTED') { 52 | next({type: requestStarted}); 53 | let nextPage = getNextPage(getState, login); 54 | let repos = getRepos(getState); 55 | getFetchData({next, login, nextPage, repos, requestSuccess, url: `${url}/starred?page=${nextPage}`}); 56 | } 57 | } 58 | 59 | 60 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-orcrist", 3 | "version": "1.0.0", 4 | "description": "siapp", 5 | "scripts": { 6 | "dev": "node build/dev-server.js", 7 | "build": "node build/build.js" 8 | }, 9 | "author": "Boyang Shang ", 10 | "license": "ISC", 11 | "dependencies": { 12 | "antd-mobile": "^1.4.0", 13 | "babel-runtime": "^6.18.0", 14 | "flex-css-layout": "^1.2.8", 15 | "history": "^3.2.1", 16 | "postcss-modules-values": "^1.2.2", 17 | "react": "^15.3.2", 18 | "react-addons-css-transition-group": "^15.4.2", 19 | "react-dom": "^15.3.2", 20 | "react-redux": "^4.4.5", 21 | "react-router": "^3.0.0", 22 | "react-router-redux": "^4.0.8", 23 | "react-router-scroll": "^0.4.1", 24 | "react-router-transition": "0.0.6", 25 | "redux": "^3.6.0", 26 | "redux-thunk": "^2.1.0" 27 | }, 28 | "devDependencies": { 29 | "autoprefixer": "^7.1.2", 30 | "babel-core": "^6.18.2", 31 | "babel-loader": "^6.2.7", 32 | "babel-plugin-import": "^1.2.1", 33 | "babel-plugin-transform-class-properties": "^6.23.0", 34 | "babel-preset-es2015": "^6.18.0", 35 | "babel-preset-latest": "^6.24.1", 36 | "babel-preset-react": "^6.16.0", 37 | "babel-preset-stage-0": "^6.24.1", 38 | "body-parser": "^1.15.2", 39 | "connect-history-api-fallback": "^1.3.0", 40 | "css-loader": "^0.25.0", 41 | "es5-shim": "^4.5.9", 42 | "es6-promise": "^4.0.5", 43 | "eventsource-polyfill": "^0.9.6", 44 | "express": "^4.14.0", 45 | "extract-text-webpack-plugin": "^1.0.1", 46 | "file-loader": "^0.9.0", 47 | "html-webpack-plugin": "^2.24.1", 48 | "http-proxy-middleware": "^0.17.2", 49 | "inject-loader": "^3.0.0-beta2", 50 | "json-loader": "^0.5.4", 51 | "jsx-loader": "^0.13.2", 52 | "less": "^2.7.2", 53 | "less-loader": "^2.2.3", 54 | "lodash": "^4.16.6", 55 | "ora": "^0.3.0", 56 | "postcss-loader": "^1.1.0", 57 | "postcss-pxtorem": "^3.3.1", 58 | "precss": "^2.0.0", 59 | "rc-form": "^1.4.3", 60 | "redux-logger": "^3.0.6", 61 | "request": "^2.76.0", 62 | "shelljs": "^0.7.5", 63 | "style-loader": "^0.13.2", 64 | "svg-sprite-loader": "^0.3.1", 65 | "url-loader": "^0.5.7", 66 | "webpack": "^1.13.3", 67 | "webpack-dev-middleware": "^1.8.4", 68 | "webpack-dev-server": "^1.16.2", 69 | "webpack-hot-middleware": "^2.13.1", 70 | "webpack-merge": "^0.15.0" 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # floor-produce 2 | action in redux applyMiddleware | 在调用链中触发action 3 | 4 | ## Profile 5 | 6 | #### synchronization actions 7 | 8 | 9 | synchronization action function used for activating asynchronous action in ` api.js ` which is composed in ` applyMiddleware `: 10 | 11 | ```js 12 | // action.js 13 | const USER_REQUEST_STARTED = 'USER_REQUEST_STARTED'; 14 | const LOAD_USER = 'LOAD_USER'; 15 | 16 | // export for components to use 17 | export const userRequest = (login) => ({ 18 | ['callFetch']: { 19 | types: [USER_REQUEST_STARTED, LOAD_USER], 20 | url: `${loginApi}${login}`, 21 | login 22 | } 23 | }); 24 | 25 | ``` 26 | 27 | #### asynchronization actions 28 | 29 | some actions in ` api.js ` will be triggered by ` next `. In this file, the `action` it's key called 'callFetch' will be triggered, but it is not factual action or asyn action and just originates other actions functions to execute, these actions will be triggered by ` next `. 30 | 31 | ```js 32 | // api.js 33 | const getFetchData = (next) => { 34 | request() 35 | .then(res => { 36 | return next({ 37 | type: 'SOME_TYPES', 38 | someData 39 | }); // asynchronization actions would be triggered 40 | }) 41 | .catch(err => { 42 | // other actions 43 | }); 44 | }; 45 | 46 | // export for applyMiddleware to catch all actions contain asyn actions 47 | export default ({getState, dispatch}) => (next) => (action) => { 48 | console.log('This is from middleware api::'); 49 | const callFetch = action['callFetch']; // catch action whose key called callFetch, you could also regard as an Object 50 | if (!callFetch) { 51 | return next(action); 52 | } 53 | const {types, url, login} = callFetch; 54 | const [requestStarted, requestSuccess] = types; 55 | if (requestStarted === 'USER_REQUEST_STARTED') { 56 | next({type: requestStarted}); 57 | getFetchData(next); 58 | // do... 59 | } else if (requestStarted === 'STARRED_REQUEST_STARTED') { 60 | next({type: requestStarted}); 61 | getFetchData(next); 62 | // do... 63 | } 64 | } 65 | 66 | ``` 67 | 68 | 69 | #### applyMiddleware 70 | 71 | `api.js` must be composed in ` applyMiddleware ` that work as expected. 72 | 73 | ```js 74 | // store.js 75 | import { createStore, applyMiddleware } from "redux"; 76 | import thunkMiddleware from "redux-thunk"; 77 | import { createLogger } from "redux-logger"; 78 | import rootReducer from "./reducer"; 79 | import api from "../middleware/api"; // api.js 80 | 81 | const createStoreWithMiddleware = applyMiddleware( 82 | thunkMiddleware, 83 | api, // in applyMiddleware 84 | createLogger(), 85 | )(createStore); 86 | 87 | ``` 88 | 89 | 90 | -------------------------------------------------------------------------------- /app/utils/request.js: -------------------------------------------------------------------------------- 1 | // es6 Promise polyfill 2 | import Promise from 'es6-promise' 3 | import {env, token, zoneCode} from '../config/env' 4 | 5 | const currEnv = env.split(';')[0]; 6 | const currHost = env.split(';')[1]; 7 | const isNative = window.App; // naitve flag 8 | 9 | /** 10 | * 为Promise扩充done 总是处于回调链最底端 保证抛出任何可能出现的异常 11 | * @param {[type]} onFulfilled [description] 12 | * @param {[type]} onRejected [description] 13 | * @return {[type]} [description] 14 | */ 15 | Promise.prototype.done = function(onFulfilled, onRejected) { 16 | this.then(onFulfilled) 17 | .catch(function(reason) { 18 | setTimeout(() => { 19 | throw reason; 20 | }, 0) 21 | }) 22 | }; 23 | 24 | const request = (type, url, params) => { 25 | 26 | // http promise flag 27 | let hasCanceled_ = false; 28 | 29 | let promise = new Promise((resolve, reject) => { 30 | 31 | type = typeof type === 'string' && type.toUpperCase(); 32 | params = params || {}; 33 | // select request type 34 | switch (true) { 35 | case type === 'GET' && currEnv !== 'native': 36 | 37 | let p = '?'; // ‘?a=xxx&b=yyy’ 38 | for (let o in params) { 39 | p += o + '=' + params[o] + '&'; 40 | } 41 | p = p.slice(0, -1); 42 | // get & http 43 | if (currHost.match(/http/g)) { 44 | url = currHost + url + p; 45 | } 46 | // proxy && native 不做任何处理 47 | break; 48 | 49 | case type === 'POST': 50 | // post & http 51 | if (currHost.match(/http/g)) { 52 | url = currHost + url; 53 | } 54 | // proxy && native 不做任何处理 55 | break; 56 | 57 | default: 58 | } 59 | 60 | /** 61 | * 区分环境执行Request 62 | */ 63 | const execute = () => { 64 | 65 | if (isNative) { 66 | 67 | const reqBody = { 68 | url : url, 69 | type : type, 70 | data : params, 71 | success: resolve, 72 | error : reject 73 | } 74 | 75 | typeof $$.Native.request === 'function' && $$.Native.request(reqBody); 76 | 77 | } else { 78 | 79 | const handler = function() { 80 | 81 | if (this.readyState !== 4) return; 82 | console.log('XMLHttpRequest::: ', this); 83 | 84 | if (this.status === 200) { 85 | resolve(this.response); 86 | } else { 87 | // alert('this.status:::' + this.status) 88 | // alert(JSON.stringify(this)); 89 | reject({hasCanceled_: true, msg: this.statusText}) 90 | } 91 | 92 | }; 93 | // alert('url:::' + url) 94 | let client = new XMLHttpRequest(); 95 | client.open(type, url); 96 | client.onreadystatechange = handler; 97 | client.responseType = 'json'; 98 | // client.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); 99 | // client.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8"); 100 | client.setRequestHeader('Accept', 'application/json'); 101 | // client.setRequestHeader('Token', token); 102 | // client.setRequestHeader('zoneCode', zoneCode); 103 | client.send(type === 'POST' ? JSON.stringify(params) : null); 104 | 105 | } 106 | } 107 | 108 | execute(); 109 | 110 | }) 111 | 112 | promise.then((data) => 113 | hasCanceled_ ? reject({hasCanceled_: true}) : resolve(data) 114 | ); 115 | 116 | promise.catch((error) => 117 | hasCanceled_ ? reject({hasCanceled_: true, error}) : reject(error) 118 | ); 119 | 120 | return { 121 | promise: promise, 122 | cancel() { 123 | hasCanceled_ = true; 124 | } 125 | }; 126 | 127 | } 128 | 129 | export default request; 130 | -------------------------------------------------------------------------------- /app/utils/pager.js: -------------------------------------------------------------------------------- 1 | /**********************************************************************/ 2 | /*************************Pager 自动分页组件*****************************/ 3 | /**********************************************************************/ 4 | ;(function(Pager) { 5 | 6 | if (typeof define === 'function' && typeof define.amd === 'object' && define.amd) { 7 | define(Pager); 8 | } else if (typeof module !== 'undefined' && module.exports) { 9 | module.exports = Pager(); 10 | } else { 11 | window.Pager = Pager(); 12 | } 13 | 14 | })(function() { 15 | 16 | function Pager(selector, option) { 17 | 18 | this.el = this.queryElements(selector); 19 | 20 | this.isLoading = false; 21 | this.hasMore = false; 22 | 23 | // 元素在可视区位置,符合其中一个条件就会触发加载机制 24 | this.option = option || {}; 25 | this.top = this.option.top || 0; //元素在顶部伸出的距离才加载 26 | this.right = this.option.right || 0; //元素在右边伸出的距离才加载 27 | this.bottom = this.option.bottom || 0; //元素在底部伸出的距离才加载 28 | this.left = this.option.left || 0; //元素在左边伸出的距离才加载 29 | 30 | // 1=页码数分规则 31 | this.pageNo = this.option.pageNo || 1; 32 | // 2-数据条下标位规则 33 | this.pageStart = this.option.start || 0; 34 | this.pageLimit = this.option.limit; 35 | 36 | // 获取数据列表 37 | this.fetchData = this.option.callback || function() { console.log('fetchData:::::::::::::::::::::::::::::::'); }; 38 | // listen 39 | this.monitorEvents = [ 40 | 'DOMContentLoaded', 'load', 'click', 41 | 'touchstart', 'touchend', 'haschange', 42 | 'online', 'pageshow', 'popstate', 43 | 'resize', 'storage', 'mousewheel', 44 | 'scroll' 45 | ]; 46 | 47 | this.init(); 48 | 49 | } 50 | 51 | Pager.prototype.init = function() { 52 | var self = this; 53 | self.eachDOM = self.eachDOM.bind(this); 54 | self.addEventsListener(self.monitorEvents); 55 | } 56 | 57 | Pager.prototype.addEventsListener = function(events) { 58 | var self = this; 59 | for (var i = 0, len = events.length; i < len; i++) { 60 | window.addEventListener(events[i], self.eachDOM, false); 61 | } 62 | self.eachDOM(); 63 | }; 64 | 65 | Pager.prototype.removeEventsListener = function() { 66 | var eventList = this.monitorEvent; 67 | for (let i = 0; i < eventList.length; i++) { 68 | window.removeEventListener(eventList[i], this.eachDOM, false); 69 | } 70 | }; 71 | 72 | /** 73 | * 遍历DOM是否符合加载条件 74 | */ 75 | Pager.prototype.eachDOM = function() { 76 | if (this.isLoading) return; 77 | for (var i = 0, len = this.el.length; i < len; i++) { 78 | if (this.isInCurrentScreen(this.el[i])) { 79 | this.execute(); 80 | return; 81 | } 82 | } 83 | } 84 | /** 85 | * 执行分页request回调 86 | */ 87 | Pager.prototype.execute = function() { 88 | 89 | var self = this; 90 | var callback = null; 91 | 92 | if (self.pageLimit > 0) { 93 | console.log('SELF PageStart in:::' + self.pageStart); 94 | callback = function(start) { 95 | self.pageStart = start; 96 | self.isLoading = false; // 97 | console.log('SELF PageStart out:::' + self.pageStart); 98 | } 99 | 100 | } else { 101 | console.log('SELF PageNo in:::' + self.pageNo); 102 | callback = function(pageNo) { 103 | self.pageNo = pageNo; 104 | self.isLoading = false; 105 | console.log('SELF PageNo out:::' + self.pageNo); 106 | } 107 | 108 | } 109 | 110 | self.isLoading = true; // 开始执行execute 111 | self.fetchData(callback); 112 | 113 | }; 114 | 115 | 116 | /** 117 | * 判断元素是否在可视区 118 | */ 119 | Pager.prototype.isInCurrentScreen = function(el) { 120 | 121 | let bcr = el.getBoundingClientRect(); //取得元素在可视区的位置 122 | let mw = el.offsetWidth; //元素自身宽度 123 | let mh = el.offsetHeight; //元素自身的高度 124 | let w = window.innerWidth; //视窗的宽度 125 | let h = window.innerHeight; //视窗的高度 126 | let boolX = (!((bcr.right - this.left) <= 0 && ((bcr.left + mw) - this.left) <= 0) && !((bcr.left + this.right) >= w && (bcr.right + this.right) >= (mw + w))); //上下符合条件 127 | let boolY = (!((bcr.bottom - this.top) <= 0 && ((bcr.top + mh) - this.top) <= 0) && !((bcr.top + this.bottom) >= h && (bcr.bottom + this.bottom) >= (mh + h))); //上下符合条件 128 | 129 | return !!(el.width != 0 && el.height != 0 && boolX && boolY); 130 | 131 | } 132 | 133 | Pager.prototype.queryElements = function(selector) { 134 | if (!selector) { 135 | new Error('Selector not found...'); 136 | return; 137 | } 138 | // console.log('selector', selector); 139 | switch (typeof selector) { 140 | case 'string': 141 | return document.querySelectorAll(selector); 142 | case 'object': 143 | if (Object.prototype.toString.call(selector) === '[object Array]') { 144 | return selector; 145 | } else { 146 | return [selector]; 147 | } 148 | } 149 | } 150 | 151 | return Pager; 152 | 153 | }) 154 | -------------------------------------------------------------------------------- /app/utils/wptr.1.1.js: -------------------------------------------------------------------------------- 1 | import Hammer from 'hammerjs' 2 | export default function WebPullToRefresh() { 3 | 'use strict'; 4 | 5 | /** 6 | * Hold all of the default parameters for the module 7 | * @type {object} 8 | */ 9 | var defaults = { 10 | // ID of the element holding pannable content area 11 | contentEl: 'content', 12 | 13 | // ID of the element holding pull to refresh loading area 14 | ptrEl: 'ptr', 15 | 16 | // wrapper element holding scollable 17 | bodyEl: document.body, 18 | 19 | // Number of pixels of panning until refresh 20 | distanceToRefresh: 70, 21 | 22 | // Pointer to function that does the loading and returns a promise 23 | loadingFunction: false, 24 | 25 | // Dragging resistance level 26 | resistance: 2.5 27 | }; 28 | 29 | /** 30 | * Hold all of the merged parameter and default module options 31 | * @type {object} 32 | */ 33 | var options = {}; 34 | 35 | /** 36 | * Pan event parameters 37 | * @type {object} 38 | */ 39 | var pan = { 40 | enabled: false, 41 | distance: 0, 42 | startingPositionY: 0 43 | }; 44 | 45 | /** 46 | * Easy shortener for handling adding and removing body classes. 47 | */ 48 | var bodyClass = defaults.bodyEl.classList; 49 | 50 | /** 51 | * Initialize pull to refresh, hammer, and bind pan events. 52 | * 53 | * @param {object=} params - Setup parameters for pull to refresh 54 | */ 55 | var init = function( params ) { 56 | params = params || {}; 57 | options = { 58 | contentEl: params.contentEl || document.getElementById( defaults.contentEl ), 59 | ptrEl: params.ptrEl || document.getElementById( defaults.ptrEl ), 60 | bodyEl: params.bodyEl || defaults.bodyEl, 61 | distanceToRefresh: params.distanceToRefresh || defaults.distanceToRefresh, 62 | loadingFunction: params.loadingFunction || defaults.loadingFunction, 63 | resistance: params.resistance || defaults.resistance, 64 | hammerOptions: params.hammerOptions || {} 65 | }; 66 | 67 | if ( ! options.contentEl || ! options.ptrEl ) { 68 | return false; 69 | } 70 | 71 | bodyClass = options.bodyEl.classList; 72 | 73 | var h = new Hammer( options.contentEl, options.hammerOptions ); 74 | 75 | h.get( 'pan' ).set( { direction: Hammer.DIRECTION_VERTICAL } ); 76 | 77 | h.on( 'panstart', _panStart ); 78 | h.on( 'pandown', _panDown ); 79 | h.on( 'panup', _panUp ); 80 | h.on( 'panend', _panEnd ); 81 | }; 82 | 83 | /** 84 | * Determine whether pan events should apply based on scroll position on panstart 85 | * 86 | * @param {object} e - Event object 87 | */ 88 | var _panStart = function(e) { 89 | pan.startingPositionY = options.bodyEl.scrollTop; 90 | 91 | if ( pan.startingPositionY === 0 ) { 92 | pan.enabled = true; 93 | } 94 | }; 95 | 96 | /** 97 | * Handle element on screen movement when the pandown events is firing. 98 | * 99 | * @param {object} e - Event object 100 | */ 101 | var _panDown = function(e) { 102 | if ( ! pan.enabled ) { 103 | return; 104 | } 105 | 106 | e.preventDefault(); 107 | pan.distance = e.distance / options.resistance; 108 | 109 | _setContentPan(); 110 | _setBodyClass(); 111 | }; 112 | 113 | /** 114 | * Handle element on screen movement when the pandown events is firing. 115 | * 116 | * @param {object} e - Event object 117 | */ 118 | var _panUp = function(e) { 119 | if ( ! pan.enabled || pan.distance === 0 ) { 120 | return; 121 | } 122 | 123 | e.preventDefault(); 124 | 125 | if ( pan.distance < e.distance / options.resistance ) { 126 | pan.distance = 0; 127 | } else { 128 | pan.distance = e.distance / options.resistance; 129 | } 130 | 131 | _setContentPan(); 132 | _setBodyClass(); 133 | }; 134 | 135 | /** 136 | * Set the CSS transform on the content element to move it on the screen. 137 | */ 138 | var _setContentPan = function() { 139 | // Use transforms to smoothly animate elements on desktop and mobile devices 140 | options.contentEl.style.transform = options.contentEl.style.webkitTransform = 'translate3d( 0, ' + pan.distance + 'px, 0 )'; 141 | options.ptrEl.style.transform = options.ptrEl.style.webkitTransform = 'translate3d( 0, ' + ( pan.distance - options.ptrEl.offsetHeight ) + 'px, 0 )'; 142 | }; 143 | 144 | /** 145 | * Set/remove the loading body class to show or hide the loading indicator after pull down. 146 | */ 147 | var _setBodyClass = function() { 148 | if ( pan.distance > options.distanceToRefresh ) { 149 | bodyClass.add( 'ptr-refresh' ); 150 | } else { 151 | bodyClass.remove( 'ptr-refresh' ); 152 | } 153 | }; 154 | 155 | /** 156 | * Determine how to animate and position elements when the panend event fires. 157 | * 158 | * @param {object} e - Event object 159 | */ 160 | var _panEnd = function(e) { 161 | if ( ! pan.enabled ) { 162 | return; 163 | } 164 | 165 | e.preventDefault(); 166 | 167 | options.contentEl.style.transform = options.contentEl.style.webkitTransform = ''; 168 | options.ptrEl.style.transform = options.ptrEl.style.webkitTransform = ''; 169 | 170 | if ( options.bodyEl.classList.contains( 'ptr-refresh' ) ) { 171 | _doLoading(); 172 | } else { 173 | _doReset(); 174 | } 175 | 176 | pan.distance = 0; 177 | pan.enabled = false; 178 | }; 179 | 180 | /** 181 | * Position content and refresh elements to show that loading is taking place. 182 | */ 183 | var _doLoading = function() { 184 | bodyClass.add( 'ptr-loading' ); 185 | 186 | // If no valid loading function exists, just reset elements 187 | if ( ! options.loadingFunction ) { 188 | return _doReset(); 189 | } 190 | 191 | // The loading function should return a promise 192 | var loadingPromise = options.loadingFunction(); 193 | 194 | // For UX continuity, make sure we show loading for at least one second before resetting 195 | setTimeout( function() { 196 | // Once actual loading is complete, reset pull to refresh 197 | loadingPromise.then( _doReset ); 198 | }, 1000 ); 199 | }; 200 | 201 | /** 202 | * Reset all elements to their starting positions before any paning took place. 203 | */ 204 | var _doReset = function() { 205 | bodyClass.remove( 'ptr-loading' ); 206 | bodyClass.remove( 'ptr-refresh' ); 207 | bodyClass.add( 'ptr-reset' ); 208 | 209 | var bodyClassRemove = function() { 210 | bodyClass.remove( 'ptr-reset' ); 211 | options.bodyEl.removeEventListener( 'transitionend', bodyClassRemove, false ); 212 | }; 213 | 214 | options.bodyEl.addEventListener( 'transitionend', bodyClassRemove, false ); 215 | }; 216 | 217 | return { 218 | init: init 219 | } 220 | 221 | } 222 | -------------------------------------------------------------------------------- /app/utils/api.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by tiwenleo on 17/10/6. 3 | */ 4 | const loginApi = 'users/'; 5 | export default loginApi; 6 | 7 | 8 | //user 9 | // { 10 | // "login": "Leotw", 11 | // "id": 12053096, 12 | // "avatar_url": "https://avatars2.githubusercontent.com/u/12053096?v=4", 13 | // "gravatar_id": "", 14 | // "url": "https://api.github.com/users/Leotw", 15 | // "html_url": "https://github.com/Leotw", 16 | // "followers_url": "https://api.github.com/users/Leotw/followers", 17 | // "following_url": "https://api.github.com/users/Leotw/following{/other_user}", 18 | // "gists_url": "https://api.github.com/users/Leotw/gists{/gist_id}", 19 | // "starred_url": "https://api.github.com/users/Leotw/starred{/owner}{/repo}", 20 | // "subscriptions_url": "https://api.github.com/users/Leotw/subscriptions", 21 | // "organizations_url": "https://api.github.com/users/Leotw/orgs", 22 | // "repos_url": "https://api.github.com/users/Leotw/repos", 23 | // "events_url": "https://api.github.com/users/Leotw/events{/privacy}", 24 | // "received_events_url": "https://api.github.com/users/Leotw/received_events", 25 | // "type": "User", 26 | // "site_admin": false, 27 | // "name": "天空", 28 | // "company": "PingAn", 29 | // "blog": "", 30 | // "location": "Shanghai", 31 | // "email": null, 32 | // "hireable": null, 33 | // "bio": null, 34 | // "public_repos": 15, 35 | // "public_gists": 0, 36 | // "followers": 46, 37 | // "following": 55, 38 | // "created_at": "2015-04-21T15:34:28Z", 39 | // "updated_at": "2017-09-28T05:59:25Z" 40 | // } 41 | 42 | //repo 43 | // [{ 44 | // "id": 10073359, 45 | // "name": "homebrew-nginx", 46 | // "full_name": "Homebrew/homebrew-nginx", 47 | // "owner": { 48 | // "login": "Homebrew", 49 | // "id": 1503512, 50 | // "avatar_url": "https://avatars2.githubusercontent.com/u/1503512?v=4", 51 | // "gravatar_id": "", 52 | // "url": "https://api.github.com/users/Homebrew", 53 | // "html_url": "https://github.com/Homebrew", 54 | // "followers_url": "https://api.github.com/users/Homebrew/followers", 55 | // "following_url": "https://api.github.com/users/Homebrew/following{/other_user}", 56 | // "gists_url": "https://api.github.com/users/Homebrew/gists{/gist_id}", 57 | // "starred_url": "https://api.github.com/users/Homebrew/starred{/owner}{/repo}", 58 | // "subscriptions_url": "https://api.github.com/users/Homebrew/subscriptions", 59 | // "organizations_url": "https://api.github.com/users/Homebrew/orgs", 60 | // "repos_url": "https://api.github.com/users/Homebrew/repos", 61 | // "events_url": "https://api.github.com/users/Homebrew/events{/privacy}", 62 | // "received_events_url": "https://api.github.com/users/Homebrew/received_events", 63 | // "type": "Organization", 64 | // "site_admin": false 65 | // }, 66 | // "private": false, 67 | // "html_url": "https://github.com/Homebrew/homebrew-nginx", 68 | // "description": ":fire_engine: Community NGINX tap for custom modules", 69 | // "fork": false, 70 | // "url": "https://api.github.com/repos/Homebrew/homebrew-nginx", 71 | // "forks_url": "https://api.github.com/repos/Homebrew/homebrew-nginx/forks", 72 | // "keys_url": "https://api.github.com/repos/Homebrew/homebrew-nginx/keys{/key_id}", 73 | // "collaborators_url": "https://api.github.com/repos/Homebrew/homebrew-nginx/collaborators{/collaborator}", 74 | // "teams_url": "https://api.github.com/repos/Homebrew/homebrew-nginx/teams", 75 | // "hooks_url": "https://api.github.com/repos/Homebrew/homebrew-nginx/hooks", 76 | // "issue_events_url": "https://api.github.com/repos/Homebrew/homebrew-nginx/issues/events{/number}", 77 | // "events_url": "https://api.github.com/repos/Homebrew/homebrew-nginx/events", 78 | // "assignees_url": "https://api.github.com/repos/Homebrew/homebrew-nginx/assignees{/user}", 79 | // "branches_url": "https://api.github.com/repos/Homebrew/homebrew-nginx/branches{/branch}", 80 | // "tags_url": "https://api.github.com/repos/Homebrew/homebrew-nginx/tags", 81 | // "blobs_url": "https://api.github.com/repos/Homebrew/homebrew-nginx/git/blobs{/sha}", 82 | // "git_tags_url": "https://api.github.com/repos/Homebrew/homebrew-nginx/git/tags{/sha}", 83 | // "git_refs_url": "https://api.github.com/repos/Homebrew/homebrew-nginx/git/refs{/sha}", 84 | // "trees_url": "https://api.github.com/repos/Homebrew/homebrew-nginx/git/trees{/sha}", 85 | // "statuses_url": "https://api.github.com/repos/Homebrew/homebrew-nginx/statuses/{sha}", 86 | // "languages_url": "https://api.github.com/repos/Homebrew/homebrew-nginx/languages", 87 | // "stargazers_url": "https://api.github.com/repos/Homebrew/homebrew-nginx/stargazers", 88 | // "contributors_url": "https://api.github.com/repos/Homebrew/homebrew-nginx/contributors", 89 | // "subscribers_url": "https://api.github.com/repos/Homebrew/homebrew-nginx/subscribers", 90 | // "subscription_url": "https://api.github.com/repos/Homebrew/homebrew-nginx/subscription", 91 | // "commits_url": "https://api.github.com/repos/Homebrew/homebrew-nginx/commits{/sha}", 92 | // "git_commits_url": "https://api.github.com/repos/Homebrew/homebrew-nginx/git/commits{/sha}", 93 | // "comments_url": "https://api.github.com/repos/Homebrew/homebrew-nginx/comments{/number}", 94 | // "issue_comment_url": "https://api.github.com/repos/Homebrew/homebrew-nginx/issues/comments{/number}", 95 | // "contents_url": "https://api.github.com/repos/Homebrew/homebrew-nginx/contents/{+path}", 96 | // "compare_url": "https://api.github.com/repos/Homebrew/homebrew-nginx/compare/{base}...{head}", 97 | // "merges_url": "https://api.github.com/repos/Homebrew/homebrew-nginx/merges", 98 | // "archive_url": "https://api.github.com/repos/Homebrew/homebrew-nginx/{archive_format}{/ref}", 99 | // "downloads_url": "https://api.github.com/repos/Homebrew/homebrew-nginx/downloads", 100 | // "issues_url": "https://api.github.com/repos/Homebrew/homebrew-nginx/issues{/number}", 101 | // "pulls_url": "https://api.github.com/repos/Homebrew/homebrew-nginx/pulls{/number}", 102 | // "milestones_url": "https://api.github.com/repos/Homebrew/homebrew-nginx/milestones{/number}", 103 | // "notifications_url": "https://api.github.com/repos/Homebrew/homebrew-nginx/notifications{?since,all,participating}", 104 | // "labels_url": "https://api.github.com/repos/Homebrew/homebrew-nginx/labels{/name}", 105 | // "releases_url": "https://api.github.com/repos/Homebrew/homebrew-nginx/releases{/id}", 106 | // "deployments_url": "https://api.github.com/repos/Homebrew/homebrew-nginx/deployments", 107 | // "created_at": "2013-05-15T07:33:45Z", 108 | // "updated_at": "2017-10-02T17:14:10Z", 109 | // "pushed_at": "2017-09-30T03:02:18Z", 110 | // "git_url": "git://github.com/Homebrew/homebrew-nginx.git", 111 | // "ssh_url": "git@github.com:Homebrew/homebrew-nginx.git", 112 | // "clone_url": "https://github.com/Homebrew/homebrew-nginx.git", 113 | // "svn_url": "https://github.com/Homebrew/homebrew-nginx", 114 | // "homepage": "", 115 | // "size": 591, 116 | // "stargazers_count": 296, 117 | // "watchers_count": 296, 118 | // "language": "Ruby", 119 | // "has_issues": true, 120 | // "has_projects": false, 121 | // "has_downloads": true, 122 | // "has_wiki": false, 123 | // "has_pages": true, 124 | // "forks_count": 109, 125 | // "mirror_url": null, 126 | // "open_issues_count": 11, 127 | // "forks": 109, 128 | // "open_issues": 11, 129 | // "watchers": 296, 130 | // "default_branch": "master" 131 | // }] 132 | 133 | 134 | --------------------------------------------------------------------------------