├── .eslintignore ├── .eslintrc.js ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── jest.config.js ├── package-lock.json ├── package.json ├── prettier.config.js ├── scripts ├── config.js ├── utils.js ├── webpack.base.conf.js ├── webpack.dev.conf.js └── webpack.prod.conf.js ├── src ├── api │ └── index.ts ├── components │ ├── App │ │ ├── index.tsx │ │ └── styles.ts │ ├── Card │ │ └── index.tsx │ ├── EasterEggs.ts │ ├── ErrorCard │ │ └── index.tsx │ ├── EventItems │ │ ├── DetailCard.tsx │ │ ├── __test__ │ │ │ ├── __snapshots__ │ │ │ │ └── eventitems.spec.tsx.snap │ │ │ ├── eventitems.spec.tsx │ │ │ └── fixtures │ │ │ │ └── DeleteEvent.json │ │ ├── index.tsx │ │ ├── styled.ts │ │ └── types.d.ts │ ├── Footer │ │ └── index.tsx │ ├── GitHubCorner.tsx │ ├── Header │ │ ├── index.tsx │ │ └── styles.ts │ ├── Input │ │ ├── index.tsx │ │ └── styles.ts │ ├── Layout │ │ └── index.ts │ ├── Loading │ │ └── index.tsx │ ├── Profile │ │ └── index.tsx │ ├── Tag │ │ └── index.tsx │ └── easing.ts ├── constants.ts ├── i18n │ ├── en-US.json │ ├── index.ts │ ├── ja-JP.json │ └── zh-CN.json ├── index.tsx ├── service │ ├── ErrorCard.ts │ ├── EventItem.ts │ ├── Header.ts │ ├── Input.ts │ ├── Loading.ts │ └── Profile.ts ├── setupTests.ts ├── store │ ├── config │ │ ├── actions.ts │ │ ├── constants.ts │ │ ├── index.ts │ │ └── types.d.ts │ ├── index.ts │ ├── main │ │ ├── actions.ts │ │ ├── constants.ts │ │ ├── index.ts │ │ └── types.d.ts │ ├── reducer.ts │ └── types.d.ts ├── styles.ts └── utils.ts └── tsconfig.json /.eslintignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | *.js 3 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | // https://eslint.org/ 2 | module.exports = { 3 | env: { 4 | es6: true, 5 | node: true, 6 | browser: true, 7 | jest: true, 8 | }, 9 | parser: '@typescript-eslint/parser', 10 | extends: [ 11 | 'plugin:@typescript-eslint/recommended', 12 | 'eslint:recommended', 13 | 'plugin:react/recommended', 14 | ], 15 | plugins: ['@typescript-eslint', 'react', 'prettier'], 16 | parserOptions: { 17 | ecmaVersion: 2018, 18 | sourceType: 'module', 19 | project: './tsconfig.json', 20 | ecmaFeatures: { 21 | jsx: true, 22 | }, 23 | }, 24 | rules: { 25 | // semi: ['error', 'never'], 26 | // 'comma-dangle': ['error', 'always-multiline'], 27 | '@typescript-eslint/indent': 0, 28 | '@typescript-eslint/camelcase': 0, 29 | '@typescript-eslint/member-delimiter-style': ['error', { multiline: { delimiter: 'none' } }], 30 | '@typescript-eslint/explicit-function-return-type': 0, 31 | '@typescript-eslint/no-explicit-any': 'off', 32 | 'linebreak-style': ['error', 'unix'], 33 | 'no-console': ['warn', { allow: ['warn', 'error'] }], 34 | 'no-undef': 'error', 35 | 'no-var': 'error', 36 | 'no-unused-vars': ['error', { args: 'none' }], 37 | 'unicode-bom': 'error', 38 | 'prefer-const': ['error', { destructuring: 'all' }], 39 | 'prettier/prettier': 'warn', 40 | }, 41 | settings: { 42 | react: { 43 | // https://github.com/yannickcr/eslint-plugin-react#configuration 44 | version: require('react').version, 45 | }, 46 | }, 47 | } 48 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # TypeScript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | .env.test 60 | 61 | # parcel-bundler cache (https://parceljs.org/) 62 | .cache 63 | 64 | # next.js build output 65 | .next 66 | 67 | # nuxt.js build output 68 | .nuxt 69 | 70 | # vuepress build output 71 | .vuepress/dist 72 | 73 | # Serverless directories 74 | .serverless/ 75 | 76 | # FuseBox cache 77 | .fusebox/ 78 | 79 | # DynamoDB Local files 80 | .dynamodb/ 81 | 82 | # production 83 | /build 84 | /dist 85 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - node 4 | cache: 5 | directories: 6 | - node_modules 7 | install: 8 | - npm install 9 | before_script: 10 | - npm run lint 11 | - npm run test -- --passWithNoTests --coverage 12 | script: 13 | - npm run build 14 | before_deploy: 15 | - cp README.md dist/ 16 | - if [ $CNAME ]; then 17 | echo $CNAME > dist/CNAME; 18 | fi 19 | deploy: 20 | - provider: pages 21 | github_token: $GITHUB_TOKEN 22 | skip-cleanup: true 23 | keep-history: false 24 | local-dir: dist 25 | on: 26 | branch: master 27 | condition: $GITHUB_TOKEN 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 lawvs 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Buddy's Github Events 2 | 3 | [![Build Status](https://travis-ci.org/lawvs/buddy-github-events.svg?branch=master)](https://travis-ci.org/lawvs/buddy-github-events) 4 | 5 | **A online website for view broadcast/received GIthub events from other people or organizations requires no login in.** 6 | 7 | ## How to Use 8 | 9 | - Input **GitHub Login Name** or **Organizations Name** in input box 10 | - Click lable next to the input box to switch github events type 11 | - Click `Search` 12 | - Example: [Recent activity by Google Organization](https://lawvs.github.io/buddy-github-events/?name=google) 13 | 14 | ## References 15 | 16 | - [parse-github-event](https://github.com/azu/parse-github-event) 17 | - [Events | GitHub Developer Guide](https://developer.github.com/v3/activity/events/) 18 | - [Event Types & Payloads | GitHub Developer Guide](https://developer.github.com/v3/activity/events/types/) 19 | - [KagamiChan/raphtalia](https://github.com/KagamiChan/raphtalia) 20 | - [Blueprint](https://blueprintjs.com/) 21 | 22 | ## License 23 | 24 | [The MIT License](LICENSE) 25 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: 'ts-jest', 3 | clearMocks: true, 4 | coverageDirectory: 'coverage', 5 | moduleFileExtensions: ['js', 'json', 'ts', 'tsx', 'node'], 6 | testMatch: ['**/__tests__/**/*.{ts,tsx}', '**/?(*.)+(spec|test).{ts,tsx}'], 7 | setupFilesAfterEnv: ['./src/setupTests.ts'], 8 | collectCoverageFrom: ['./src/**/*.{ts,tsx}'], 9 | } 10 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "buddy-github-events", 3 | "version": "1.0.0", 4 | "description": "Check out your buddy's github events", 5 | "scripts": { 6 | "start": "npm run dev", 7 | "dev": "webpack-dev-server --config scripts/webpack.dev.conf.js --mode=development --colors --watch --hot", 8 | "build": "webpack --config scripts/webpack.prod.conf.js --mode=production --colors", 9 | "lint": "eslint --ext .ts,.tsx .", 10 | "lint:fix": "eslint --ext .ts,.tsx --fix .", 11 | "clean": "rm -rf dist", 12 | "test": "jest" 13 | }, 14 | "repository": { 15 | "type": "git", 16 | "url": "git+https://github.com/lawvs/buddy-github-events.git" 17 | }, 18 | "author": "lawvs", 19 | "license": "MIT", 20 | "bugs": { 21 | "url": "https://github.com/lawvs/buddy-github-events/issues" 22 | }, 23 | "homepage": "https://github.com/lawvs/buddy-github-events", 24 | "dependencies": { 25 | "i18next": "^15.1.0", 26 | "parse-github-event": "^1.1.3", 27 | "react": "^16.12.0", 28 | "react-dom": "^16.12.0", 29 | "react-i18next": "^10.9.0", 30 | "react-markdown": "^8.0.3", 31 | "react-redux": "^7.1.3", 32 | "react-timeago": "^4.4.0", 33 | "redux": "^4.0.5", 34 | "redux-thunk": "^2.3.0", 35 | "styled-components": "^5.0.0" 36 | }, 37 | "devDependencies": { 38 | "@types/i18next": "^12.1.0", 39 | "@types/jest": "^24.0.25", 40 | "@types/node": "^12.12.24", 41 | "@types/react": "^16.9.17", 42 | "@types/react-dom": "^16.9.4", 43 | "@types/react-redux": "^7.1.5", 44 | "@types/react-test-renderer": "^16.9.1", 45 | "@types/react-timeago": "^4.1.1", 46 | "@types/styled-components": "^4.4.2", 47 | "@typescript-eslint/eslint-plugin": "^2.16.0", 48 | "@typescript-eslint/parser": "^2.16.0", 49 | "awesome-typescript-loader": "^5.2.1", 50 | "clean-webpack-plugin": "^2.0.2", 51 | "copy-webpack-plugin": "^11.0.0", 52 | "eslint": "^6.8.0", 53 | "eslint-plugin-prettier": "^3.1.2", 54 | "eslint-plugin-react": "^7.17.0", 55 | "html-webpack-plugin": "^5.5.0", 56 | "html-webpack-template": "^6.2.0", 57 | "jest": "^29.3.0", 58 | "prettier": "^1.19.1", 59 | "react-test-renderer": "^16.12.0", 60 | "ts-jest": "^29.0.3", 61 | "typescript": "^3.7.4", 62 | "webpack": "^4.41.5", 63 | "webpack-cli": "^3.3.12", 64 | "webpack-dev-server": "^4.11.1", 65 | "webpack-merge": "^4.2.2" 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /prettier.config.js: -------------------------------------------------------------------------------- 1 | // https://prettier.io/docs/en/install.html 2 | module.exports = { 3 | semi: false, 4 | singleQuote: true, 5 | trailingComma: 'all', 6 | printWidth: 100, 7 | // bracketSpacing: true, 8 | // tabWidth: 2, 9 | // useTabs: false, 10 | } 11 | -------------------------------------------------------------------------------- /scripts/config.js: -------------------------------------------------------------------------------- 1 | const { resolvePath } = require('./utils') 2 | const { description } = require(resolvePath('package.json')) 3 | 4 | module.exports = { 5 | port: 3000, // dev server port 6 | publicUrl: process.env.PUBLIC_URL || '/', 7 | // @see https://github.com/jaketrent/html-webpack-template 8 | templateConfig: { 9 | title: "Buddy's Github Events", 10 | appMountId: 'root', 11 | mobile: true, 12 | // @see https://github.com/joshbuchea/HEAD#meta 13 | meta: [ 14 | { 15 | name: 'description', 16 | content: description, 17 | }, 18 | ], 19 | }, 20 | } 21 | -------------------------------------------------------------------------------- /scripts/utils.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | 3 | exports.resolvePath = function(dir = '') { 4 | return path.join(__dirname, '..', dir) 5 | } 6 | -------------------------------------------------------------------------------- /scripts/webpack.base.conf.js: -------------------------------------------------------------------------------- 1 | const HtmlWebpackPlugin = require('html-webpack-plugin') 2 | const HtmlWebpackTemplate = require('html-webpack-template') 3 | 4 | const { resolvePath } = require('./utils') 5 | const { templateConfig } = require('./config') 6 | 7 | /** 8 | * @type {import('webpack').Configuration} 9 | */ 10 | module.exports = { 11 | mode: process.env.NODE_ENV, 12 | entry: resolvePath('src/index.tsx'), 13 | resolve: { 14 | extensions: ['.ts', '.tsx', '.js', '.jsx', 'json'], 15 | }, 16 | module: { 17 | rules: [ 18 | { 19 | test: /\.(png|jpe?g|gif|svg)$/, 20 | use: [ 21 | { 22 | loader: 'url-loader', 23 | options: { 24 | limit: 10000, 25 | name: 'images/[name].[chunkhash:7].[ext]', 26 | }, 27 | }, 28 | ], 29 | }, 30 | { 31 | test: /\.tsx?$/, 32 | loader: 'awesome-typescript-loader', 33 | }, 34 | ], 35 | }, 36 | plugins: [ 37 | // @see https://github.com/ampedandwired/html-webpack-plugin 38 | new HtmlWebpackPlugin({ 39 | inject: false, 40 | template: HtmlWebpackTemplate, 41 | ...templateConfig, 42 | minify: { 43 | removeComments: true, 44 | collapseWhitespace: true, 45 | }, 46 | }), 47 | ], 48 | } 49 | -------------------------------------------------------------------------------- /scripts/webpack.dev.conf.js: -------------------------------------------------------------------------------- 1 | const merge = require('webpack-merge') 2 | 3 | const baseWebpackConfig = require('./webpack.base.conf') 4 | const { port } = require('./config') 5 | const { resolvePath } = require('./utils') 6 | 7 | /** 8 | * @type {import('webpack').Configuration} 9 | */ 10 | const webpackConfig = merge(baseWebpackConfig, { 11 | mode: 'development', 12 | // @see https://webpack.js.org/configuration/devtool/ 13 | devtool: 'cheap-module-eval-source-map', 14 | output: { 15 | publicPath: '/', 16 | }, 17 | devServer: { 18 | port, 19 | open: true, 20 | historyApiFallback: true, 21 | contentBase: resolvePath('public'), // static assets 22 | }, 23 | performance: { 24 | hints: false, 25 | }, 26 | }) 27 | 28 | module.exports = webpackConfig 29 | -------------------------------------------------------------------------------- /scripts/webpack.prod.conf.js: -------------------------------------------------------------------------------- 1 | const url = require('url') 2 | const merge = require('webpack-merge') 3 | const CleanWebpackPlugin = require('clean-webpack-plugin') 4 | const CopyWebpackPlugin = require('copy-webpack-plugin') 5 | 6 | const baseWebpackConfig = require('./webpack.base.conf') 7 | const { resolvePath } = require('./utils') 8 | 9 | const { publicUrl } = require('./config') 10 | const publicPath = url.parse(publicUrl).pathname 11 | 12 | /** 13 | * @type {import('webpack').Configuration} 14 | */ 15 | const webpackConfig = merge(baseWebpackConfig, { 16 | mode: 'production', 17 | bail: true, 18 | devtool: process.env.CI ? 'source-map' : false, 19 | output: { 20 | filename: '[name].[chunkhash:8].js', 21 | publicPath, 22 | }, 23 | plugins: [ 24 | new CleanWebpackPlugin(), 25 | new CopyWebpackPlugin([ 26 | { 27 | from: resolvePath('public'), // static assets 28 | to: '[name].[ext]', 29 | }, 30 | ]), 31 | ], 32 | }) 33 | 34 | module.exports = webpackConfig 35 | -------------------------------------------------------------------------------- /src/api/index.ts: -------------------------------------------------------------------------------- 1 | import { GithubApi } from 'parse-github-event/lib/types' 2 | 3 | const GITHUB_API = 'https://api.github.com' 4 | 5 | export const fetchGithubProfileApi = (name: string): Promise => 6 | fetch(`${GITHUB_API}/users/${name}`).then(resp => { 7 | if (!resp.ok) { 8 | throw new Error(resp.statusText) 9 | } 10 | return resp.json() as Promise 11 | }) 12 | 13 | export enum GithubEventsType { 14 | EVENTS, 15 | RECEIVED_EVENTS, 16 | } 17 | 18 | export const GitHubEventsDefaultPerPage = 30 19 | 20 | // @see https://developer.github.com/v3/activity/events/#list-events-that-a-user-has-received 21 | export const fetchGithubEventsApi = ( 22 | name: string, 23 | type: GithubEventsType, 24 | // pagination @see https://developer.github.com/v3/#pagination 25 | page = 1, 26 | perPage = GitHubEventsDefaultPerPage, // paginated to 30 items by default, page size up to 100 27 | ): Promise => 28 | fetch( 29 | type === GithubEventsType.EVENTS 30 | ? `${GITHUB_API}/users/${name}/events?page=${page}&per_page=${perPage}` 31 | : `${GITHUB_API}/users/${name}/received_events?page=${page}&per_page=${perPage}`, 32 | ).then(resp => { 33 | if (!resp.ok) { 34 | throw new Error(resp.statusText) 35 | } 36 | return resp.json() as Promise 37 | }) 38 | -------------------------------------------------------------------------------- /src/components/App/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import EventItems from '../../service/EventItem' 4 | import Profile from '../../service/Profile' 5 | import Loading from '../../service/Loading' 6 | import Footer from '../Footer' 7 | import ErrorCard from '../../service/ErrorCard' 8 | import Header from '../../service/Header' 9 | import { AppWrapper, Main, PlaceHolder, Center } from './styles' 10 | 11 | const App = () => ( 12 | 13 |
14 |
15 | 16 |
17 | 18 | 19 | 20 |
21 | 22 |
23 |