├── .babelrc
├── .editorconfig
├── .eslintignore
├── .eslintrc
├── .gitignore
├── LICENSE
├── README.md
├── package.json
├── src
├── img
│ ├── icon-128.png
│ ├── icon-16.png
│ └── icon-64.png
├── js
│ ├── State.js
│ └── contentscript.js
└── manifest.json
├── webpack.config.js
└── yarn.lock
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | [
4 | "@babel/preset-env",
5 | {
6 | "targets": {
7 | "browsers": ["last 2 versions", "safari >= 7"]
8 | }
9 | }
10 | ]
11 | ],
12 | "plugins": ["@babel/plugin-transform-runtime"]
13 | }
14 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | indent_style = space
5 | end_of_line = lf
6 | charset = utf-8
7 | trim_trailing_whitespace = true
8 | insert_final_newline = true
9 | indent_size = 2
10 |
11 | [*.md]
12 | trim_trailing_whitespace = false
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | **/node_modules/**
2 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "parser": "babel-eslint",
3 | "extends": ["eslint:recommended", "airbnb", "prettier"],
4 | "globals": {
5 | "chrome": true
6 | },
7 | "env": {
8 | "browser": true,
9 | "node": true,
10 | "jest": true
11 | },
12 | "rules": {
13 | "prettier/prettier": [
14 | "error",
15 | {
16 | "trailingComma": "es5",
17 | "singleQuote": true
18 | }
19 | ],
20 | "no-param-reassign": "off",
21 | "func-names": "off"
22 | },
23 | "plugins": ["babel", "prettier"]
24 | }
25 |
--------------------------------------------------------------------------------
/.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 (http://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 (http://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 |
60 | # build
61 | build
62 |
63 | # zip
64 | zip
65 |
66 | # .DS_Store
67 | .DS_Store
68 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 - present Homer Chen
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 | # GitHub Unfold News
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | > Starting from someday, news are folded on GitHub dashboard page.
11 | > I do't really like it, so I build this browser extension to control them.
12 |
13 | ## Screen shot
14 |
15 | 
16 |
17 | ## Install
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | ## License
28 |
29 | MIT © [xxhomey19](https://github.com/xxhomey19)
30 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "github-unfold-news",
3 | "description": "",
4 | "license": "MIT",
5 | "author": "xxhomey19",
6 | "version": "0.1.1",
7 | "main": "index.js",
8 | "scripts": {
9 | "build": "cross-env NODE_ENV=production webpack --config webpack.config.js --display-error-details --progress --colors",
10 | "precommit": "lint-staged",
11 | "dev": "cross-env NODE_ENV=development webpack --config ./webpack.config.js --watch",
12 | "lint": "eslint src",
13 | "lint:fix": "npm run lint -- --fix",
14 | "zip:all": "rimraf zip && run-s build zip:chrome zip:firefox",
15 | "zip:chrome": "mkdirp zip/chrome && zip -r zip/chrome/GITHUB_UNFOLD_ACTIVITY_$npm_package_version.zip build/",
16 | "zip:firefox": "mkdirp zip/firefox && web-ext build -s build/ -a zip/firefox"
17 | },
18 | "devDependencies": {
19 | "@babel/core": "^7.1.0",
20 | "@babel/plugin-transform-runtime": "^7.1.0",
21 | "@babel/preset-env": "^7.1.0",
22 | "@babel/runtime": "^7.0.0",
23 | "babel-eslint": "^9.0.0",
24 | "babel-loader": "^8.0.2",
25 | "clean-webpack-plugin": "^0.1.19",
26 | "copy-webpack-plugin": "^4.5.2",
27 | "cross-env": "^5.2.0",
28 | "eslint": "^5.6.0",
29 | "eslint-config-airbnb": "^17.1.0",
30 | "eslint-config-prettier": "^3.1.0",
31 | "eslint-plugin-babel": "^5.2.0",
32 | "eslint-plugin-import": "^2.14.0",
33 | "eslint-plugin-jsx-a11y": "^6.1.1",
34 | "eslint-plugin-prettier": "^2.6.2",
35 | "eslint-plugin-react": "^7.11.1",
36 | "html-loader": "^0.5.5",
37 | "husky": "^1.0.0-rc.15",
38 | "lint-staged": "^7.3.0",
39 | "mkdirp": "^0.5.1",
40 | "npm-run-all": "^4.1.3",
41 | "prettier": "^1.14.3",
42 | "prettier-package-json": "^2.0.1",
43 | "rimraf": "^2.6.2",
44 | "uglifyjs-webpack-plugin": "^2.0.1",
45 | "web-ext": "^2.9.1",
46 | "webpack": "^4.19.1",
47 | "webpack-cli": "^3.1.1",
48 | "webpack-dev-server": "^3.1.8",
49 | "write-file-webpack-plugin": "^4.4.0"
50 | },
51 | "lint-staged": {
52 | "package.json": [
53 | "prettier-package-json --write",
54 | "git add"
55 | ],
56 | "*.js": [
57 | "eslint --fix",
58 | "git add"
59 | ]
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/img/icon-128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/homerchen19/github-unfold-news/fa40a1ffdccf671d631968c4d42266728d10bf67/src/img/icon-128.png
--------------------------------------------------------------------------------
/src/img/icon-16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/homerchen19/github-unfold-news/fa40a1ffdccf671d631968c4d42266728d10bf67/src/img/icon-16.png
--------------------------------------------------------------------------------
/src/img/icon-64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/homerchen19/github-unfold-news/fa40a1ffdccf671d631968c4d42266728d10bf67/src/img/icon-64.png
--------------------------------------------------------------------------------
/src/js/State.js:
--------------------------------------------------------------------------------
1 | class State {
2 | constructor(state) {
3 | this.state = state;
4 | }
5 |
6 | getState() {
7 | return this.state;
8 | }
9 |
10 | setState(newState) {
11 | this.state = {
12 | ...this.state,
13 | ...newState,
14 | };
15 | }
16 | }
17 |
18 | export default State;
19 |
--------------------------------------------------------------------------------
/src/js/contentscript.js:
--------------------------------------------------------------------------------
1 | import State from './State';
2 |
3 | const detailsContainerSelector =
4 | 'div.Details.js-details-container.js-news-feed-event-group';
5 |
6 | const state = new State({
7 | unfoldAll: true,
8 | });
9 |
10 | const unfold = () => {
11 | const { unfoldAll } = state.getState();
12 | const detailsContainer = Array.from(
13 | document.querySelectorAll(detailsContainerSelector)
14 | );
15 |
16 | if (detailsContainer.length !== 0) {
17 | for (let i = 0; i < detailsContainer.length; i += 1) {
18 | const element = detailsContainer[i];
19 |
20 | if (unfoldAll) {
21 | element.classList.add('Details--on');
22 | } else {
23 | element.classList.remove('Details--on');
24 | }
25 | }
26 | }
27 | };
28 |
29 | const changeText = element => {
30 | const { unfoldAll } = state.getState();
31 |
32 | element.innerHTML = !unfoldAll ? 'Fold All' : 'Unfold All';
33 |
34 | state.setState({
35 | unfoldAll: !unfoldAll,
36 | });
37 | };
38 |
39 | const createButton = () => {
40 | const { unfoldAll } = state.getState();
41 | const button = document.createElement('a');
42 |
43 | button.classList.add(
44 | 'btn',
45 | 'btn-sm',
46 | 'btn-primary',
47 | 'text-white',
48 | 'float-right'
49 | );
50 | button.innerHTML = unfoldAll ? 'Fold All' : 'Unfold All';
51 | button.addEventListener('click', () => {
52 | changeText(button);
53 | unfold();
54 | });
55 |
56 | return button;
57 | };
58 |
59 | const addButton = () => {
60 | const activityHeader = document.querySelector('.js-all-activity-header');
61 | const button = createButton();
62 |
63 | activityHeader.classList.add('mb-1');
64 | activityHeader.appendChild(button);
65 | };
66 |
67 | const init = () => {
68 | const target = document.getElementsByClassName('news')[0];
69 |
70 | const observer = new MutationObserver(() => {
71 | unfold();
72 | });
73 |
74 | observer.observe(target, {
75 | attributes: true,
76 | childList: true,
77 | characterData: true,
78 | });
79 | };
80 |
81 | addButton();
82 | init();
83 |
--------------------------------------------------------------------------------
/src/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "manifest_version": 2,
3 | "name": "GitHub Unfold News",
4 | "description": "Auto unfold news on GitHub dashboard.",
5 | "author": "xxhomey19",
6 | "content_scripts": [
7 | {
8 | "matches": ["https://github.com/"],
9 | "js": ["contentscript.bundle.js"]
10 | }
11 | ],
12 | "browser_action": {
13 | "default_icon": "img/icon-64.png"
14 | },
15 | "icons": {
16 | "16": "img/icon-16.png",
17 | "64": "img/icon-64.png",
18 | "128": "img/icon-128.png"
19 | },
20 | "content_security_policy": "script-src 'self'; object-src 'self'"
21 | }
22 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const webpack = require('webpack');
2 | const path = require('path');
3 | const CleanWebpackPlugin = require('clean-webpack-plugin');
4 | const CopyWebpackPlugin = require('copy-webpack-plugin');
5 | const WriteFilePlugin = require('write-file-webpack-plugin');
6 | const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
7 |
8 | const options = {
9 | entry: {
10 | contentscript: path.join(__dirname, 'src', 'js', 'contentscript.js'),
11 | },
12 | output: {
13 | path: path.join(__dirname, 'build'),
14 | filename: '[name].bundle.js',
15 | },
16 | module: {
17 | rules: [
18 | {
19 | test: /\.js$/,
20 | loader: 'babel-loader',
21 | exclude: /node_modules/,
22 | },
23 | {
24 | test: /\.html$/,
25 | loader: 'html-loader',
26 | exclude: /node_modules/,
27 | },
28 | ],
29 | },
30 | plugins: [
31 | new CleanWebpackPlugin(['build']),
32 | new webpack.DefinePlugin({
33 | 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
34 | }),
35 | new CopyWebpackPlugin([
36 | {
37 | from: 'src/manifest.json',
38 | transform: content =>
39 | Buffer.from(
40 | JSON.stringify({
41 | description: process.env.npm_package_description,
42 | version: process.env.npm_package_version,
43 | ...JSON.parse(content.toString()),
44 | })
45 | ),
46 | },
47 | {
48 | from: 'src/img',
49 | to: 'img',
50 | },
51 | ]),
52 | new WriteFilePlugin(),
53 | ],
54 | };
55 |
56 | if (process.env.NODE_ENV === 'development') {
57 | options.mode = 'development';
58 | options.devtool = 'cheap-module-source-map';
59 | } else if (process.env.NODE_ENV === 'production') {
60 | options.mode = 'production';
61 | options.plugins.push(
62 | new webpack.LoaderOptionsPlugin({
63 | minimize: true,
64 | debug: false,
65 | }),
66 | new UglifyJSPlugin({
67 | sourceMap: true,
68 | parallel: true,
69 | })
70 | );
71 | }
72 |
73 | module.exports = options;
74 |
--------------------------------------------------------------------------------