├── 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
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 |

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 |
--------------------------------------------------------------------------------