XboxYan' Blog
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "notes",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "gitalk": "^1.5.0",
7 | "highlightjs": "^9.12.0",
8 | "moment": "^2.24.0",
9 | "pm2": "^2.10.4",
10 | "react": "^16.8.6",
11 | "react-aplayer": "^1.0.0",
12 | "react-dom": "^16.8.6",
13 | "react-keeper": "^2.1.12",
14 | "react-loadable": "^5.4.0",
15 | "react-scripts": "^3.0.1",
16 | "remarkable": "^1.7.1"
17 | },
18 | "proxy": "https://api.github.com/graphql",
19 | "homepage": "https://blog.codelabo.cn/build",
20 | "scripts": {
21 | "start": "react-scripts start",
22 | "build": "react-scripts build",
23 | "test": "react-scripts test --env=jsdom",
24 | "eject": "react-scripts eject"
25 | },
26 | "browserslist": {
27 | "production": [
28 | ">0.2%",
29 | "not dead",
30 | "not op_mini all"
31 | ],
32 | "development": [
33 | "last 1 chrome version",
34 | "last 1 firefox version",
35 | "last 1 safari version"
36 | ]
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/public/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/XboxYan/notes/8814a8957ec0f6e98f25ec547361f539c4fdb3b1/public/favicon.png
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
11 |
12 |
13 |
22 | XboxYan' Blog
23 |
161 |
162 |
163 |
166 |
167 |
168 |
178 |
179 |
180 |
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | }
10 | ],
11 | "start_url": "./index.html",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/src/App.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent, Fragment } from 'react';
2 | import { BrowserRouter } from 'react-keeper';
3 |
4 | import Index from './pages';
5 | import ReactAplayer from 'react-aplayer';
6 | import evanyou from './util/evanyou';
7 | import './App.css';
8 |
9 |
10 |
11 | class App extends PureComponent {
12 |
13 | getPlayList = (id) => {
14 | return fetch(`/api/playlist/${id}`)
15 | .then((response) => {
16 | if (response.ok) {
17 | return response.json() || {};
18 | }else{
19 | return {};
20 | }
21 | })
22 | .catch((err) => {
23 | console.warn(err);
24 | return {};
25 | })
26 | }
27 |
28 | async componentDidMount() {
29 | evanyou();
30 | //const data = await this.getPlayList('21711688');
31 | //this.alpayer.list.add(data.data||[])
32 | }
33 |
34 |
35 | render() {
36 | return (
37 |
38 |
39 |
40 | this.alpayer = ap}
42 | theme="#e26d6d"
43 | order="random"
44 | lrcType={3}
45 | fixed={true}
46 | mini={true}
47 | listFolded={true}
48 | audio={[]}
49 | />
50 |
51 |
52 | );
53 | }
54 | }
55 |
56 | export default App;
57 |
--------------------------------------------------------------------------------
/src/admin.css:
--------------------------------------------------------------------------------
1 | .table-con{
2 | width: 100%;
3 | }
4 |
5 | .table-con th{
6 |
7 | background: #e26d6d;
8 | color: #fff;
9 | height: 40px;
10 | border: 0;
11 | }
12 |
13 | .table-con td{
14 | text-align: center;
15 | word-wrap: break-word;
16 | word-break: break-all;
17 | border: 0;
18 | height: 40px;
19 | }
20 |
21 | .table-con tbody tr{
22 | transition: .2s;
23 | border-bottom: 1px solid #eee;
24 | }
25 |
26 | .table-con tbody tr:hover{
27 | background: #f9f9f9
28 | }
29 |
30 | .table-con tbody tr td:nth-child(2),.table-con tbody tr td:nth-child(5){
31 | text-align: left;
32 | }
33 |
34 | .table-con .tag-wrap{
35 | line-height: inherit;
36 | padding: 3px 12px;
37 | margin: 3px;
38 | }
39 |
40 | .table-con .article-tag{
41 | margin: 3px;
42 | }
43 |
44 | .admin-container{
45 | position: relative;
46 | z-index: 1;
47 | background: #fff;
48 | }
49 |
50 | .admin-container .main{
51 | width: 98%;
52 | padding-top: 1rem;
53 | }
54 |
55 | .admin-title{
56 | margin: 1rem 0;
57 | letter-spacing: .6px;
58 | color: rgba(0,0,0,.5);
59 | font-size: 1.25rem;
60 | }
61 |
62 | .admin-container .edit-input{
63 | border: 1px solid #eee;
64 | padding: 15px 10px;
65 | border-radius: 5px;
66 | resize: none;
67 | }
68 |
69 | .admin-container .edit-input:focus{
70 | border-color: #e26d6d
71 | }
72 |
73 | .checkbox{
74 | position: absolute;
75 | clip: rect(0,0,0,0)
76 | }
77 |
78 | .checkbox+.article-tag{
79 | position: relative;
80 | transition: .2s;
81 | }
82 |
83 | .checkbox:checked+.article-tag{
84 | border: 1px solid #e26d6d;
85 | color: #e26d6d;
86 | }
87 |
88 | .checkbox:checked+.article-tag:after{
89 | content: '√';
90 | display: inline-block;
91 | margin-left: 5px;
92 | font-size: small;
93 | transform: rotate(10deg)
94 | }
95 |
96 | .article-tag-loading{
97 | opacity: 0.6;
98 | pointer-events: none;
99 | }
100 |
101 | .admin-content{
102 | padding-bottom: 1.5rem;
103 | }
104 |
105 | .admin-add-button{
106 | cursor: pointer;
107 | margin-bottom: 1.5rem;
108 | display: inline-block;
109 | padding: 3px 6px;
110 | color: #e26d6d;
111 | border-radius: 4px;
112 | border: 1px solid #e26d6d;
113 | transition: .2s;
114 | font-size: .85rem;
115 | }
116 |
117 | .admin-add-button:hover{
118 | background: #e26d6d;
119 | color: #fff;
120 | }
121 |
122 |
--------------------------------------------------------------------------------
/src/admin/article.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react';
2 | import fetchData from '../util/Fetch';
3 | import Footer from '../components/footer';
4 | import Loader from '../components/loader';
5 | import Pager from '../components/pager';
6 | import moment from 'moment';
7 | import { CacheLink } from 'react-keeper';
8 |
9 | export default class extends PureComponent {
10 | pagesize = 5;
11 |
12 | state = {
13 | articles:[],
14 | total:0,
15 | isrender:true
16 | }
17 |
18 | getArticle = async (page=1) => {
19 | this.setState({isrender:true});
20 | const articles = await fetchData(`/api/article?page=${page}&pagesize=${this.pagesize}`);
21 | this.setState({articles:articles.data,total:articles.counts,isrender:false});
22 | }
23 |
24 | onhandle = (dir) => () => {
25 | const { total, page } = this.state;
26 | let $page = Math.max(Math.min(page + dir,Math.ceil(total/this.pagesize)),1);
27 | this.getArticle($page);
28 | }
29 |
30 | componentDidMount () {
31 | this.getArticle(1);
32 | }
33 |
34 | render() {
35 | const {articles,total,isrender} = this.state;
36 | return (
37 |
38 |
39 | 共{total}篇文章
40 |
41 |
42 |
43 | _id |
44 | 标题 |
45 | 发布时间 |
46 | 访问量 |
47 | 分类 |
48 | 操作 |
49 |
50 |
51 |
52 | {
53 | !isrender && articles.map(article=>(
54 |
55 | {article._id} |
56 | {article.title} |
57 | {moment(article.createdAt).utcOffset(8).format("YYYY年M月D日 , HH:mm:ss")} |
58 | {article.views} |
59 | {article.categories.map((category,i)=>{category.name})} |
60 |
61 | 编辑
62 | 删除
63 | |
64 |
65 | ))
66 | }
67 |
68 |
69 | {
70 | isrender&&
71 | }
72 | 现在发布
73 |
78 |
79 |
80 |
81 | )
82 | }
83 | }
--------------------------------------------------------------------------------
/src/admin/category.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react';
2 | import fetchData from '../util/Fetch';
3 | import Footer from '../components/footer';
4 | import Loader from '../components/loader';
5 | import moment from 'moment';
6 | import { CacheLink } from 'react-keeper';
7 |
8 | class Edit extends PureComponent {
9 |
10 | state = {
11 | show:false,
12 | title:'',
13 | }
14 |
15 | show = (oname,title,cb) => {
16 | this.input.value = oname;
17 | this.setState({show:true,title});
18 | this.input.focus();
19 | this.onSubmit = (ev) => {
20 | ev.preventDefault();
21 | cb(this.input.value.replace(/\s+/g, ""),this.hide);
22 | }
23 | }
24 |
25 | hide = () => {
26 | this.setState({show:false});
27 | }
28 |
29 | render() {
30 | const { show,title } = this.state;
31 | return (
32 |
33 |
37 |
38 | )
39 | }
40 | }
41 |
42 | export default class extends PureComponent {
43 |
44 | state = {
45 | categories: [],
46 | isrender: true
47 | }
48 |
49 | getCategories = async () => {
50 | this.setState({ isrender: true });
51 | const categories = await fetchData(`/api/category`);
52 | this.setState({ categories: categories.data, isrender: false });
53 | }
54 |
55 | componentDidMount() {
56 | this.getCategories();
57 | }
58 |
59 | onEdit = (id,oname) => () => {
60 | this.popedit.show(oname,'修改分类',async(name,cb)=>{
61 | if(name){
62 | const categoryInfo = await fetchData(`/api/category/${id}`,{
63 | method:'PUT',
64 | body:JSON.stringify({name})
65 | });
66 | if(categoryInfo.success){
67 | await this.getCategories();
68 | cb&&cb();
69 | }
70 | }
71 | })
72 | }
73 |
74 | onAdd = () => {
75 | this.popedit.show('','新增分类',async(name,cb)=>{
76 | if(name){
77 | const categoryInfo = await fetchData(`/api/category`,{
78 | method:'POST',
79 | body:JSON.stringify({name})
80 | });
81 | if(categoryInfo.success){
82 | await this.getCategories();
83 | cb&&cb();
84 | }
85 | }
86 | })
87 | }
88 |
89 | onDel = (id) => async () => {
90 | const categoryInfo = await fetchData(`/api/category/${id}`,{
91 | method:'DELETE'
92 | });
93 | if(categoryInfo.success){
94 | await this.getCategories();
95 | }
96 | }
97 |
98 | render() {
99 | const { categories, isrender } = this.state;
100 | return (
101 |
102 |
103 | 共{categories.length}个分类
104 |
105 |
106 |
107 | _id |
108 | 名称 |
109 | 创建时间 |
110 | 更新时间 |
111 | 操作 |
112 |
113 |
114 |
115 | {
116 | !isrender && categories.map(category => (
117 |
118 | {category._id} |
119 | {category.name} |
120 | {moment(category.createdAt).utcOffset(8).format("YYYY年M月D日 , HH:mm:ss")} |
121 | {moment(category.updatedAt).utcOffset(8).format("YYYY年M月D日 , HH:mm:ss")} |
122 |
123 | 编辑
124 | 删除
125 | |
126 |
127 | ))
128 | }
129 |
130 |
131 | {
132 | isrender &&
133 | }
134 | 添加分类
135 |
136 |
137 |
this.popedit=node} />
138 |
139 | )
140 | }
141 | }
--------------------------------------------------------------------------------
/src/admin/home.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react';
2 | import Footer from '../components/footer';
3 |
4 | export default class extends PureComponent {
5 | render() {
6 | return (
7 |
8 |
11 |
12 |
13 | )
14 | }
15 | }
--------------------------------------------------------------------------------
/src/admin/index.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react';
2 | import { Route } from 'react-keeper';
3 | import Loadable from 'react-loadable';
4 | import Nav from '../components/nav';
5 | import Loader from '../components/loader';
6 | import '../admin.css';
7 |
8 | const Home = Loadable({
9 | loader:()=>import('./home'),
10 | loading:Loader
11 | })
12 |
13 | const Article = Loadable({
14 | loader:()=>import('./article'),
15 | loading:Loader
16 | })
17 |
18 | const Category = Loadable({
19 | loader:()=>import('./category'),
20 | loading:Loader
21 | })
22 |
23 | const Publish = Loadable({
24 | loader:()=>import('./publish'),
25 | loading:Loader
26 | })
27 |
28 | export default class extends PureComponent {
29 | render() {
30 | return (
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 | );
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/admin/publish.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent, Fragment } from 'react';
2 | import { Control } from 'react-keeper';
3 | import fetchData from '../util/Fetch';
4 | import Markview from '../components/markview';
5 | import BackTop from '../components/backTop';
6 |
7 | class CategoryGroup extends PureComponent {
8 |
9 | state = {
10 | checked: this.props.checked,
11 | categories:[]
12 | }
13 |
14 | getCategories = async () => {
15 | this.setState({ isrender: true });
16 | const categories = await fetchData(`/api/category`);
17 | this.setState({ categories: categories.data });
18 | }
19 |
20 | componentWillReceiveProps(nextProps) {
21 | if( this.props.checked!==nextProps.checked){
22 | this.setState({checked:nextProps.checked})
23 | }
24 | }
25 |
26 | onChange = (ev) => {
27 | const checked = [...this.state.checked];
28 | const target = ev.target;
29 | if (target.checked) {
30 | checked.push(target.id);
31 | } else {
32 | checked.splice(checked.findIndex((d) => d === target.id), 1);
33 | }
34 | this.props.onChange && this.props.onChange(checked);
35 | this.setState({ checked });
36 | }
37 |
38 | componentDidMount() {
39 | this.getCategories();
40 | }
41 |
42 | render() {
43 | const { checked,categories } = this.state;
44 | this.value = checked;
45 | return (
46 |
47 | {
48 | categories.length > 0 ? categories.map((d) => (
49 |
50 | = 0} id={d._id} />
51 |
52 |
53 | ))
54 | :
55 |
56 | }
57 |
58 | )
59 | }
60 | }
61 |
62 | class MarkdownEditor extends PureComponent {
63 |
64 | state = {
65 | value: this.props.defaultValue
66 | };
67 |
68 | onChange = (e) => {
69 | const value = e.target.value;
70 | this.timer && clearTimeout(this.timer);
71 | this.timer = setTimeout(() => {
72 | this.setState({ value });
73 | this.props.onChange && this.props.onChange(value);
74 | }, 300)
75 | }
76 |
77 | componentWillReceiveProps(nextProps) {
78 | if( this.props.defaultValue!==nextProps.defaultValue){
79 | this.setState({value:nextProps.defaultValue});
80 | this.textarea.value = nextProps.defaultValue;
81 | }
82 | }
83 |
84 | onkeydown = (e) => {
85 | if(e.keyCode===9){
86 | e.preventDefault();
87 | const target = e.target;
88 | const value = target.value;
89 | const positionS = target.selectionStart;
90 | const positionE = target.selectionEnd;
91 | target.value = value.substr(0,positionS)+' '+value.substr(positionE);
92 | target.selectionStart = positionS+2;
93 | target.selectionEnd = positionS+2;
94 | }
95 | }
96 |
97 | render() {
98 | const { value } = this.state;
99 | this.value = value;
100 | const { className,rows,placeholder } = this.props;
101 | return (
102 |
106 | )
107 | }
108 | }
109 |
110 | export default class extends PureComponent {
111 | state = {
112 | isrender: true,
113 | article:{},
114 | }
115 |
116 |
117 | getArticle = async (id) => {
118 | this.setState({isrender:true});
119 | const article = await fetchData(`/api/article/${id}?admin=true`);
120 | this.setState({ article: article.data, isrender: false });
121 | this.title.value = article.data.title || '';
122 | }
123 |
124 | componentDidMount() {
125 | this.id = Control.path.split('publish/')[1];
126 | if(this.id){
127 | this.getArticle(this.id);
128 | }
129 | //this.getArticle();
130 | }
131 |
132 | componentWillReceiveProps(nextProps) {
133 | if( Control.path.indexOf('publish/')>=0 && this.props.pathname!==nextProps.pathname && this.id!== Control.path.split('publish/')[1]){
134 | this.id = Control.path.split('publish/')[1];
135 | this.getArticle(this.id);
136 | }else{
137 | this.id = null;
138 | this.title.value = '';
139 | this.setState({ article: {}});
140 | }
141 | }
142 |
143 | onSubmit = async () => {
144 | const article = {
145 | title:this.title.value,
146 | categories:this.categories.value,
147 | description:this.description.value,
148 | content:this.content.value,
149 | }
150 | if(this.id){
151 | //更新
152 | const articleInfo = await fetchData(`/api/article/${this.id}`,{
153 | method:'PUT',
154 | body:JSON.stringify(article)
155 | });
156 | if(articleInfo.success){
157 | console.log('success1')
158 | Control.go(-1);
159 | }
160 | }else{
161 | article.userId = "5b34af1cf532152535c6c03a"
162 | //发布
163 | const articleInfo = await fetchData(`/api/article`,{
164 | method:'POST',
165 | body:JSON.stringify(article)
166 | });
167 | if(articleInfo.success){
168 | console.log('success2')
169 | Control.go(-1);
170 | }
171 | }
172 | }
173 |
174 | goTop = () => {
175 | document.getElementById("publish-con").scrollTo({top:0,behavior: 'smooth' })
176 | }
177 |
178 | render() {
179 | const {article:{categories=[],description='',content=''}} = this.state;
180 |
181 | return (
182 |
183 |
184 | 标题
185 | this.title=node} spellCheck={false} className="edit-input" />
186 | 分类
187 | this.categories=node} checked={categories} />
188 | 简介
189 | this.description=node} rows={3} placeholder="一些简介~" defaultValue={description} />
190 | 正文
191 | this.content=node} className="admin-content" rows={10} placeholder="开始写文章吧!" defaultValue={content} />
192 | 立即发布
193 |
194 |
195 |
196 | )
197 | }
198 | }
--------------------------------------------------------------------------------
/src/components/back.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Control } from 'react-keeper';
3 |
4 | export default () => (
5 | Control.go(-1)}>
6 | )
--------------------------------------------------------------------------------
/src/components/backTop.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react';
2 |
3 | export default class extends PureComponent {
4 | render() {
5 | return (
6 |
7 |
8 |
9 |
10 |
11 | )
12 | }
13 | }
--------------------------------------------------------------------------------
/src/components/comment.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react';
2 | import { Control } from 'react-keeper';
3 | import 'gitalk/dist/gitalk.css'
4 | import Gitalk from 'gitalk'
5 |
6 | export default class extends PureComponent {
7 | componentDidMount() {
8 | const number = Control.path.split('article/')[1];
9 | var gitalk = new Gitalk({
10 | clientID: 'b36eae2069001417b114',
11 | clientSecret: '22b5da9b1fd92cca7268dfd0eaf4305ee427f0f2',
12 | repo: 'notes',
13 | owner: 'XboxYan',
14 | admin: ['XboxYan'],
15 | number: Number(number),
16 | //id: window.location.pathname, // Ensure uniqueness and length less than 50
17 | distractionFreeMode: false // Facebook-like distraction free mode
18 | })
19 | gitalk.render('gitalk-container')
20 | }
21 | render() {
22 | return (
23 |
24 | )
25 | }
26 | }
--------------------------------------------------------------------------------
/src/components/donate.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent,Fragment } from 'react';
2 |
3 | export default class extends PureComponent {
4 | render() {
5 | return (
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
})
14 |
})
15 |
16 |
世界美好 你也是
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | )
25 | }
26 | }
--------------------------------------------------------------------------------
/src/components/footer.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export default () => (
4 |
11 | )
--------------------------------------------------------------------------------
/src/components/header.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | //import User from './user';
3 | import { Link, Control } from 'react-keeper';
4 |
5 | export default ({loginState,userInfo,logout}) => (
6 |
29 | )
--------------------------------------------------------------------------------
/src/components/loader.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react';
2 |
3 | export default class extends PureComponent {
4 | render(){
5 | return (
6 |
7 | )
8 | }
9 | }
--------------------------------------------------------------------------------
/src/components/markide.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react';
2 | import hljs from 'highlightjs';
3 |
4 | export default class extends PureComponent {
5 |
6 | state = {
7 | value: 'Hello, **world**!'
8 | };
9 |
10 | onChange = (e) => {
11 | const value = e.target.value;
12 | this.setState({ value });
13 | this.props.onChange && this.props.onChange(value);
14 | }
15 |
16 | getRawMarkup = (value) => {
17 | hljs.configure({ classPrefix: '' });
18 | return { __html: hljs.highlight('markdown', value).value };
19 | }
20 | render(){
21 | const {value} = this.state;
22 | const {rows,placeholder} = this.props;
23 | return (
24 |
28 | )
29 | }
30 | }
--------------------------------------------------------------------------------
/src/components/markview.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Remarkable from 'remarkable';
3 | import hljs from 'highlightjs';
4 | import '../mark.css';
5 |
6 | export default (props) => {
7 | const getRawMarkup = (value) => {
8 | const md = new Remarkable({
9 | html: true,
10 | breaks: true,
11 | typographer: true,
12 | langPrefix: '',
13 | highlight: function (str, lang) {
14 | if (lang && hljs.getLanguage(lang)) {
15 | try {
16 | hljs.configure({ classPrefix: '' });
17 | return hljs.highlight(lang, str).value;
18 | } catch (err) { }
19 | }
20 |
21 | try {
22 | hljs.configure({ classPrefix: '' });
23 | return hljs.highlightAuto(str).value;
24 | } catch (err) { }
25 |
26 | return ''; // use external default escaping
27 | }
28 | });
29 | md.core.ruler.enable([
30 | 'abbr'
31 | ]);
32 | md.inline.ruler.enable([
33 | 'footnote_inline',
34 | 'ins',
35 | 'mark',
36 | 'sub',
37 | 'sup'
38 | ]);
39 | return { __html: md.render(value) };
40 | }
41 | return (
42 |
43 | )
44 | }
--------------------------------------------------------------------------------
/src/components/nav.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Link } from 'react-keeper';
3 |
4 | export default () => (
5 |
15 | )
--------------------------------------------------------------------------------
/src/components/pager.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react';
2 |
3 | export default class extends PureComponent {
4 | state = {
5 | page: 1,
6 | }
7 |
8 | go = (dir) => () => {
9 | const { page } = this.state;
10 | const { total, pagesize,pageInfo:{startCursor,endCursor} } = this.props;
11 | let $page = Math.max(Math.min(page + dir, Math.ceil(total / pagesize)), 1);
12 | this.setState({ isrender: true, page:$page });
13 | this.props.fetch && this.props.fetch(dir>0?{after:endCursor}:{before:startCursor});
14 | }
15 |
16 | render() {
17 | const { total, pagesize } = this.props;
18 | const max = Math.ceil(total / pagesize);
19 | const { page } = this.state;
20 | return (
21 | total?
22 |
27 | :null
28 | )
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/components/profile.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react';
2 |
3 | export default class extends PureComponent {
4 | render() {
5 | return (
6 |
7 |
})
8 |
XboxYan
9 |
23 |
24 | )
25 | }
26 | }
--------------------------------------------------------------------------------
/src/components/user.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react';
2 | import { Link } from 'react-keeper';
3 |
4 | export default class extends PureComponent {
5 | render() {
6 | const { userInfo,logout } = this.props;
7 | return (
8 |
9 |
{userInfo.username[0]}
10 |
{userInfo.username}
11 |
12 | {
13 | userInfo.isAdmin &&
进入后台
14 | }
15 |
退出
16 |
17 |
18 | )
19 | }
20 | }
--------------------------------------------------------------------------------
/src/fonts/fonts.gstatic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/XboxYan/notes/8814a8957ec0f6e98f25ec547361f539c4fdb3b1/src/fonts/fonts.gstatic.woff2
--------------------------------------------------------------------------------
/src/fonts/iconfont.e0fd.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/XboxYan/notes/8814a8957ec0f6e98f25ec547361f539c4fdb3b1/src/fonts/iconfont.e0fd.woff
--------------------------------------------------------------------------------
/src/img/about.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/XboxYan/notes/8814a8957ec0f6e98f25ec547361f539c4fdb3b1/src/img/about.jpg
--------------------------------------------------------------------------------
/src/img/alipay.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/XboxYan/notes/8814a8957ec0f6e98f25ec547361f539c4fdb3b1/src/img/alipay.jpg
--------------------------------------------------------------------------------
/src/img/header.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/XboxYan/notes/8814a8957ec0f6e98f25ec547361f539c4fdb3b1/src/img/header.png
--------------------------------------------------------------------------------
/src/img/wechat.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/XboxYan/notes/8814a8957ec0f6e98f25ec547361f539c4fdb3b1/src/img/wechat.png
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 | import registerServiceWorker from './registerServiceWorker';
5 |
6 | ReactDOM.render(, document.getElementById('root'));
7 | registerServiceWorker();
8 |
--------------------------------------------------------------------------------
/src/login/index.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react';
2 | import Back from '../components/back';
3 | import fetchData from '../util/Fetch';
4 |
5 | export default class extends PureComponent {
6 |
7 | state = {
8 | username:window.localStorage.loginInfo ? JSON.parse(window.localStorage.loginInfo).username:'',
9 | password:''
10 | }
11 |
12 | onSubmit = async (ev) => {
13 | ev.preventDefault();
14 | const { username,password } = this.state;
15 | const loginInfo = await fetchData(`/api/login`,{
16 | method:'POST',
17 | body:JSON.stringify({username,password})
18 | });
19 | if(loginInfo.success){
20 | window.localStorage.loginInfo = JSON.stringify(loginInfo.info);
21 | this.props.login(loginInfo.info);
22 | }
23 | }
24 |
25 | onChange = (name) => (ev) => {
26 | this.setState({[name]:ev.target.value})
27 | }
28 |
29 | render() {
30 | const { username,password } = this.state;
31 | return (
32 |
33 |
34 |
42 |
43 | );
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/mark.css:
--------------------------------------------------------------------------------
1 | hr {
2 | margin: 1em 0;
3 | border:0;
4 | border-top: 1px solid #eee;
5 | }
6 |
7 | ol,ul {
8 | margin-left: 22px;
9 | padding-left: 0
10 | }
11 |
12 | ol p,ul p {
13 | margin: 0
14 | }
15 |
16 | li {
17 | font-size: 15px;
18 | margin: 8px 0;
19 | list-style-type: none;
20 | position: relative
21 | }
22 |
23 | ul li:before {
24 | position: absolute;
25 | left: -12px;
26 | top: 0;
27 | content: "-";
28 | width: 2px;
29 | height: 2px
30 | }
31 |
32 | ol {
33 | counter-reset:count;
34 | }
35 |
36 | ol li:before {
37 | position: absolute;
38 | left: -15px;
39 | top: 0;
40 | content: counter(count) '.';
41 | counter-increment: count;
42 | width: 2px;
43 | height: 2px
44 | }
45 |
46 | pre {
47 | overflow: auto;
48 | margin: 20px 0;
49 | font-size: 13.5px;
50 | color: #4d4d4c;
51 | background: #fcfdfd
52 | }
53 |
54 | code,.code,pre {
55 | font-family: consolas
56 | }
57 |
58 | code, {
59 | padding: 2px 4px;
60 | word-wrap: break-word;
61 | font-size: 13.5px
62 | }
63 |
64 | pre code {
65 | padding: 0;
66 | color: #4d4d4c;
67 | background: none;
68 | text-shadow: none
69 | }
70 |
71 | pre {
72 | padding: 10px;
73 | margin: 20px 0;
74 | border-radius: 6px;
75 | text-shadow: 1px 2px 1px rgba(0,0,0,.009);
76 | box-shadow: 0 1px 2px rgba(0,0,0,.05)
77 | }
78 |
79 | pre table {
80 | margin: 0;
81 | width: auto;
82 | border: none
83 | }
84 |
85 | pre td {
86 | border: none;
87 | padding: 0
88 | }
89 |
90 | pre figcaption {
91 | font-size: 1em;
92 | color: #4d4d4c;
93 | line-height: 1em;
94 | margin-bottom: 1em
95 | }
96 |
97 | pre figcaption:after,.highlight figcaption:before {
98 | content: " ";
99 | display: table
100 | }
101 |
102 | pre figcaption:after {
103 | clear: both
104 | }
105 |
106 | pre figcaption a {
107 | float: right;
108 | color: #4d4d4c
109 | }
110 |
111 | pre figcaption a:hover {
112 | border-bottom-color: #4d4d4c
113 | }
114 |
115 | pre .gutter pre {
116 | margin: 0;
117 | text-align: right;
118 | font-size: 12px;
119 | color: #606a6d;
120 | font-family: Arial,"sans-serif"
121 | }
122 |
123 | pre .gutter pre {
124 | padding-left: 10px;
125 | padding-right: 10px;
126 | background-color: #fcfdfd
127 | }
128 |
129 | pre pre {
130 | width: 100%
131 | }
132 |
133 | pre .line, pre hr {
134 | display: inline-block;
135 | padding: 2px 0;
136 | height: 20px;
137 | border: none
138 | }
139 |
140 | pre .marked {
141 | width: 100%;
142 | border-left: 2px solid #727272;
143 | margin: .5em 0;
144 | padding-left: .5em
145 | }
146 |
147 | .gutter {
148 | user-select: none
149 | }
150 |
151 | .gist table {
152 | width: auto
153 | }
154 |
155 | .gist table td {
156 | border: none
157 | }
158 |
159 | pre .deletion {
160 | background: #fdd
161 | }
162 |
163 | pre .addition {
164 | background: #dfd
165 | }
166 |
167 | pre .meta, pre .quote {
168 | color: #8959a8
169 | }
170 |
171 | .code {
172 | color: #4271ae
173 | }
174 |
175 | pre .comment {
176 | color: #c6c6c6
177 | }
178 |
179 | pre .attribute,pre .name,pre .css .class,pre .css .id,pre .css .pseudo,pre .html .doctype,pre .regexp,pre .ruby .constant,pre .variable,pre .xml .doctype,pre .xml .pi,pre .xml .tag .title {
180 | color: #e24c38
181 | }
182 |
183 | pre .tag{
184 | color: #8e908c
185 | }
186 |
187 | pre .attr, pre .bullet{
188 | color: #3e999f
189 | }
190 |
191 | pre .built_in,pre .symbol,pre .command,pre .constant,pre .literal,pre .number,pre .params,pre preprocessor {
192 | color: #e78c45
193 | }
194 |
195 | pre .formula,pre .header,pre .inheritance,pre .number,pre .ruby .class .title,pre .ruby .symbol,pre .special,pre .string,pre .value,pre .xml .cdata {
196 | color: #718c00
197 | }
198 |
199 | pre .css .hexcolor,pre .title {
200 | color: #3e999f
201 | }
202 |
203 | pre .css .hexcolor,pre .title {
204 | color: #3e999f
205 | }
206 |
207 | pre .css .selector-class {
208 | color: #3e999f
209 | }
210 |
211 | pre .css .attribute {
212 | color: #8293ef
213 | }
214 |
215 | pre .coffeescript .title,pre .function,pre .javascript .title,pre .perl .sub,pre .python .decorator,pre .python .title,pre .ruby .function .title,pre .ruby .title .keyword {
216 | color: #4271ae
217 | }
218 |
219 | pre .javascript .function,pre .keyword, pre .section {
220 | color: #e24c38
221 | }
222 |
223 | pre {
224 | position: relative;
225 | width: 100%
226 | }
227 |
228 | pre .emphasis{
229 | font-style: italic;
230 | color: #718c00
231 | }
232 |
233 | pre .strong{
234 | font-weight: bold;
235 | color: #8293ef
236 | }
237 |
238 | pre .link{
239 | text-decoration: underline;
240 | color: #e78c45
241 | }
242 |
243 | pre code:after {
244 | position: absolute;
245 | top: 0;
246 | right: 0;
247 | color: #ccc;
248 | content: "Code";
249 | font-size: 12px;
250 | padding: 5px 10px 0;
251 | height: 15px
252 | }
253 |
254 | code.html:after {
255 | content: "HTML"
256 | }
257 |
258 | code.javascript:after,code.js:after {
259 | content: "JS"
260 | }
261 |
262 | code.bash:after,code.sh:after,code.cmd:after,code.Powershell:after {
263 | content: "Shell"
264 | }
265 |
266 | code.css:after {
267 | content: "CSS"
268 | }
269 |
270 | code.sass:after {
271 | content: "SASS"
272 | }
273 |
274 | code.less:after {
275 | content: "LESS"
276 | }
277 |
278 | code.style:after {
279 | content: "Style"
280 | }
281 |
282 | code.json:after {
283 | content: "JSON"
284 | }
285 |
286 | code.markdown:after {
287 | content: "MD"
288 | }
289 |
290 | code.diff:after {
291 | content: "DIFF"
292 | }
293 |
294 | code.java:after {
295 | content: "Java"
296 | }
297 |
298 | code.c:after {
299 | content: "C"
300 | }
301 |
302 | code.yml:after {
303 | content: "YML"
304 | }
305 |
306 | code.php:after {
307 | content: "PHP"
308 | }
309 |
310 | code.react:after {
311 | content: "REACT"
312 | }
313 |
314 | p code,h1 code,h2 code,h3 code,h4 code,h5 code,h6 code,li code {
315 | padding: 2px 5px;
316 | text-shadow: 1px 2px 1px rgba(0,0,0,.02);
317 | box-shadow: 0 2px 2px rgba(0,0,0,.05);
318 | color: #e26d6d;
319 | margin: 0 .4em;
320 | background: #fdfcfc
321 | }
322 |
323 | .preview thead{
324 | background: #fcfdfd
325 | }
--------------------------------------------------------------------------------
/src/pages/about.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react';
2 | import Footer from '../components/footer';
3 |
4 | export default class extends PureComponent {
5 | render() {
6 | return (
7 |
8 |
9 | 关于
10 | })
11 |
12 | Drawing by Alena Aenami.
13 |
14 |
About Me
15 |
16 | 世界美好 你也是。
17 | Theme - Nayo
18 | Location - HuBei | WuHan CN
19 | Email - yanwenbin1991@live.com
20 |
21 |
22 |
23 |
24 |
25 |
26 | )
27 | }
28 | }
--------------------------------------------------------------------------------
/src/pages/archives.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent,Fragment } from 'react';
2 | import { CacheLink } from 'react-keeper';
3 | import fetchData from '../util/Fetch';
4 | import Loader from '../components/loader';
5 | import Footer from '../components/footer';
6 | import moment from 'moment';
7 |
8 | export default class extends PureComponent {
9 | state = {
10 | articles:[],
11 | isrender: true
12 | }
13 |
14 | getArticle = async () => {
15 | const articles = await fetchData('/api/archives');
16 | this.setState({articles:articles.data,isrender:false});
17 | }
18 |
19 | componentDidMount() {
20 | this.getArticle();
21 | }
22 |
23 | render() {
24 | const {articles,isrender} = this.state;
25 | return (
26 |
27 |
28 | 归档
29 |
30 | {
31 | isrender?
32 |
33 | :
34 | articles.map((d,i)=>(
35 |
36 | { moment(d.createdAt).utcOffset(8).format("YYYY年M月") }
37 | {
38 | d.article.map(article=>(
39 |
40 | {moment(article.createdAt).utcOffset(8).format("M月D日")}
41 |
42 | {article.title}
43 |
44 |
45 | ))
46 | }
47 |
48 | ))
49 | }
50 |
51 |
52 |
53 |
54 | )
55 | }
56 | }
--------------------------------------------------------------------------------
/src/pages/article.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react';
2 | import fetchData from '../util/Fetch';
3 | import { Control,CacheLink } from 'react-keeper';
4 | //import Donate from '../components/donate';
5 | import Loader from '../components/loader';
6 | import Footer from '../components/footer';
7 | import Comment from '../components/comment';
8 | import Markview from '../components/markview';
9 | import { getArticleDetail } from '../util/api';
10 | import moment from 'moment';
11 |
12 | export default class extends PureComponent {
13 |
14 | state = {
15 | article: {},
16 | isrender: true
17 | }
18 |
19 | componentDidMount() {
20 | const id = Control.path.split('article/')[1];
21 | this.getArticle(id);
22 | }
23 |
24 | getArticle = async (id) => {
25 | this.setState({isrender:true});
26 | const { data:{repository:{issue}} } = await getArticleDetail({number:id});
27 | this.setState({ article: issue, isrender: false });
28 | }
29 |
30 | componentWillReceiveProps(nextProps) {
31 | if( this.props.pathname.indexOf('article')>=0 && nextProps.pathname.indexOf('article')>=0 && this.props.pathname!==nextProps.pathname){
32 | const {id} = nextProps.params;
33 | this.getArticle(id);
34 | }
35 | }
36 |
37 | render() {
38 | const { article:{ title,createdAt,body,labels,prev,next }, isrender } = this.state;
39 | return (
40 |
41 |
42 | {
43 | isrender?
44 |
45 | :
46 |
47 |
48 |
{title}
49 |
50 | {moment(createdAt).utcOffset(8).format("YYYY年M月D日 , HH:mm:ss")}
51 | {0}
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 | {
60 | labels.nodes.length>0?
61 | labels.nodes.map((category, i) => {category.name})
62 | :
63 | 未分类
64 | }
65 |
66 |
67 | }
68 |
69 | {
70 | /*
71 |
72 |
73 |
})
74 |
hi,i am XboxYan
75 |
76 |
77 |
78 |
79 |
80 | {
81 | !isrender&&
82 |
90 | }
91 |
92 |
93 | */
94 | }
95 |
96 |
97 |
98 | )
99 | }
100 | }
--------------------------------------------------------------------------------
/src/pages/categories.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react';
2 | import { Control,CacheLink,Route } from 'react-keeper';
3 | import Footer from '../components/footer';
4 | import Loader from '../components/loader';
5 | import Pager from '../components/pager';
6 | import fetchData from '../util/Fetch';
7 | import { getArticleList,getCategory } from '../util/api';
8 | import moment from 'moment';
9 |
10 | class Category extends PureComponent {
11 |
12 | pagesize = 5;
13 |
14 | state = {
15 | isrender:true,
16 | total:1,
17 | renderPage:true,
18 | pageInfo:{},
19 | articles:[]
20 | }
21 |
22 | getArticle = (category='') => async (page=1) => {
23 | if(category){
24 | this.setState({isrender:true});
25 | const { data:{repository:{issues:{nodes,pageInfo,totalCount}}} } = await getArticleList({pagesize:this.pagesize,labels:category});
26 | this.setState({articles:nodes,total:totalCount,isrender:false,pageInfo:pageInfo,renderPage:true});
27 | }
28 | }
29 |
30 | async componentDidMount() {
31 | const category = Control.path.split("/categories/")[1]||"";
32 | this.category = category;
33 | this.getArticle(category)();
34 | }
35 |
36 | componentWillReceiveProps(nextProps) {
37 | if( this.category!==nextProps.params.category && nextProps.pathname.indexOf('search')<0 && nextProps.pathname.indexOf('categories/')>=0 && this.props.pathname!==nextProps.pathname){
38 | const {category} = nextProps.params;
39 | this.category = category;
40 | this.setState({renderPage:false});
41 | this.getArticle(category)();
42 | }
43 | }
44 |
45 | render(){
46 | const {articles,total,isrender,renderPage,pageInfo} = this.state;
47 | const category = this.category||"";
48 | return (
49 |
50 | {
51 | isrender?
52 |
53 | :
54 | (
55 | articles.length>0?
56 | articles.map(article=>(
57 |
58 |
59 | {article.title}
60 |
61 | ))
62 | :
63 |
64 | )
65 | }
66 | {
67 | renderPage&&
72 | }
73 |
74 | )
75 | }
76 | }
77 |
78 | export default class extends PureComponent {
79 | state = {
80 | categories: []
81 | }
82 |
83 | async componentDidMount() {
84 | const { data:{repository:{labels:{nodes}}} }= await getCategory();
85 | this.setState({ categories: nodes });
86 | }
87 |
88 | render() {
89 | const {categories} = this.state;
90 | return (
91 |
92 |
93 |
94 | 分类
95 | {
96 | categories.filter(el=>!el.isDefault).map(d=>(
97 |
98 |
99 | {d.name}
100 |
101 | ))
102 | }
103 |
104 |
105 |
106 |
107 |
108 | )
109 | }
110 | }
--------------------------------------------------------------------------------
/src/pages/home.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react';
2 | import Loader from '../components/loader';
3 | import Profile from '../components/profile';
4 | import BackTop from '../components/backTop';
5 | import Footer from '../components/footer';
6 | import Pager from '../components/pager';
7 | import fetchData from '../util/Fetch';
8 | import { getArticleList } from '../util/api';
9 | import { CacheLink } from 'react-keeper';
10 | import moment from 'moment';
11 |
12 | export default class extends PureComponent {
13 |
14 | pagesize = 5;
15 |
16 | state = {
17 | articles:[],
18 | total:1,
19 | isrender:true,
20 | pageInfo:{}
21 | }
22 |
23 | getArticle = async (options) => {
24 | this.goTop();
25 | this.setState({isrender:true});
26 | const { data:{repository:{issues:{nodes,pageInfo,totalCount}}} } = await getArticleList({pagesize:this.pagesize,...options});
27 | // console.log(data)
28 | // const articles = await fetchData(`/api/article?page=${page}&pagesize=${this.pagesize}`);
29 | this.setState({articles:nodes,total:totalCount,pageInfo:pageInfo,isrender:false});
30 | }
31 |
32 | toIndex = () => {
33 | this.getArticle({});
34 | this.Pager.setState({page:1})
35 | }
36 |
37 | componentDidMount () {
38 | this.getArticle({});
39 | }
40 |
41 | goTop = () => {
42 | document.getElementById("index-con").scrollTo({top:0,behavior: 'smooth' })
43 | }
44 |
45 | render() {
46 | const {articles,total,isrender,pageInfo} = this.state;
47 | return (
48 |
49 |
50 |
51 | {
52 | isrender?
53 |
54 | :
55 | articles.map((article)=>(
56 |
57 |
58 |
{article.title}
59 |
{ moment(article.createdAt).utcOffset(8).format("YYYY年M月D日") }
60 | {
61 | article.labels.nodes.length>0
62 | ?
63 | article.labels.nodes.map((category,i)=>(
64 | {category.name}
65 | ))
66 | :
67 | 未分类
68 | }
69 |
70 |
71 |
72 | {article.description}
73 |
74 |
75 | 阅读更多
76 |
77 |
78 | ))
79 | }
80 | this.Pager=node}
82 | total={total}
83 | pageInfo={pageInfo}
84 | pagesize={this.pagesize}
85 | fetch={this.getArticle}
86 | />
87 |
88 |
89 |
90 |
91 | )
92 | }
93 | }
--------------------------------------------------------------------------------
/src/pages/index.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react';
2 | import { Route } from 'react-keeper';
3 | import Header from '../components/header';
4 | import Loadable from 'react-loadable';
5 | import Loader from '../components/loader';
6 |
7 | const Home = Loadable({
8 | loader:()=>import('./home'),
9 | loading:Loader
10 | })
11 |
12 | /*
13 | const Archives = Loadable({
14 | loader:()=>import('./archives'),
15 | loading:Loader
16 | })
17 | */
18 |
19 | const Categories = Loadable({
20 | loader:()=>import('./categories'),
21 | loading:Loader
22 | })
23 |
24 | const Search = Loadable({
25 | loader:()=>import('./search'),
26 | loading:Loader
27 | })
28 |
29 | const About = Loadable({
30 | loader:()=>import('./about'),
31 | loading:Loader
32 | })
33 |
34 | const Article = Loadable({
35 | loader:()=>import('./article'),
36 | loading:Loader
37 | })
38 |
39 | export default class extends PureComponent {
40 | render() {
41 | return (
42 |
43 |
44 |
45 | {
46 | //
47 | }
48 |
49 |
50 |
51 |
52 |
53 | );
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/pages/search.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react';
2 | import Loader from '../components/loader';
3 | import fetchData from '../util/Fetch';
4 | import { Control,Link } from 'react-keeper';
5 |
6 | export default class extends PureComponent {
7 |
8 | state = {
9 | data: [],
10 | keywords: '',
11 | isrender: true
12 | }
13 |
14 | getArticles = async (keywords) => {
15 | this.setState({ isrender: true });
16 | const articles = await fetchData('/api/search?keywords=' + keywords);
17 | this.setState({ data: articles.data, isrender: false });
18 | }
19 |
20 | search = (ev) => {
21 | const { value } = ev.target;
22 | const keywords = value.replace(/\s+/g, "");
23 | this.setState({ keywords });
24 | this.timer && clearTimeout(this.timer);
25 | this.timer = setTimeout(() => {
26 | keywords&&this.getArticles(keywords);
27 | }, 500)
28 | }
29 |
30 | render() {
31 | const { keywords, data, isrender } = this.state;
32 | return (
33 |
34 |
35 |
36 |
37 | Control.go(-1)}>
38 |
39 |
40 | {
41 | keywords && (
42 | isrender ?
43 |
44 | :
45 | (
46 | data.length > 0 ?
47 | data.map((d) => (
48 |
49 |
{d.title}
50 |
{d.description}
51 |
52 | ))
53 | :
54 |
55 | )
56 | )
57 | }
58 |
59 |
60 |
61 | )
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/registerServiceWorker.js:
--------------------------------------------------------------------------------
1 | // In production, we register a service worker to serve assets from local cache.
2 |
3 | // This lets the app load faster on subsequent visits in production, and gives
4 | // it offline capabilities. However, it also means that developers (and users)
5 | // will only see deployed updates on the "N+1" visit to a page, since previously
6 | // cached resources are updated in the background.
7 |
8 | // To learn more about the benefits of this model, read https://goo.gl/KwvDNy.
9 | // This link also includes instructions on opting out of this behavior.
10 |
11 | const isLocalhost = Boolean(
12 | window.location.hostname === 'localhost' ||
13 | // [::1] is the IPv6 localhost address.
14 | window.location.hostname === '[::1]' ||
15 | // 127.0.0.1/8 is considered localhost for IPv4.
16 | window.location.hostname.match(
17 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
18 | )
19 | );
20 |
21 | export default function register() {
22 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
23 | // The URL constructor is available in all browsers that support SW.
24 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location);
25 | if (publicUrl.origin !== window.location.origin) {
26 | // Our service worker won't work if PUBLIC_URL is on a different origin
27 | // from what our page is served on. This might happen if a CDN is used to
28 | // serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374
29 | return;
30 | }
31 |
32 | window.addEventListener('load', () => {
33 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
34 |
35 | if (isLocalhost) {
36 | // This is running on localhost. Lets check if a service worker still exists or not.
37 | checkValidServiceWorker(swUrl);
38 |
39 | // Add some additional logging to localhost, pointing developers to the
40 | // service worker/PWA documentation.
41 | navigator.serviceWorker.ready.then(() => {
42 | console.log(
43 | 'This web app is being served cache-first by a service ' +
44 | 'worker. To learn more, visit https://goo.gl/SC7cgQ'
45 | );
46 | });
47 | } else {
48 | // Is not local host. Just register service worker
49 | registerValidSW(swUrl);
50 | }
51 | });
52 | }
53 | }
54 |
55 | function registerValidSW(swUrl) {
56 | navigator.serviceWorker
57 | .register(swUrl)
58 | .then(registration => {
59 | registration.onupdatefound = () => {
60 | const installingWorker = registration.installing;
61 | installingWorker.onstatechange = () => {
62 | if (installingWorker.state === 'installed') {
63 | if (navigator.serviceWorker.controller) {
64 | // At this point, the old content will have been purged and
65 | // the fresh content will have been added to the cache.
66 | // It's the perfect time to display a "New content is
67 | // available; please refresh." message in your web app.
68 | console.log('New content is available; please refresh.');
69 | } else {
70 | // At this point, everything has been precached.
71 | // It's the perfect time to display a
72 | // "Content is cached for offline use." message.
73 | console.log('Content is cached for offline use.');
74 | }
75 | }
76 | };
77 | };
78 | })
79 | .catch(error => {
80 | console.error('Error during service worker registration:', error);
81 | });
82 | }
83 |
84 | function checkValidServiceWorker(swUrl) {
85 | // Check if the service worker can be found. If it can't reload the page.
86 | fetch(swUrl)
87 | .then(response => {
88 | // Ensure service worker exists, and that we really are getting a JS file.
89 | if (
90 | response.status === 404 ||
91 | response.headers.get('content-type').indexOf('javascript') === -1
92 | ) {
93 | // No service worker found. Probably a different app. Reload the page.
94 | navigator.serviceWorker.ready.then(registration => {
95 | registration.unregister().then(() => {
96 | window.location.reload();
97 | });
98 | });
99 | } else {
100 | // Service worker found. Proceed as normal.
101 | registerValidSW(swUrl);
102 | }
103 | })
104 | .catch(() => {
105 | console.log(
106 | 'No internet connection found. App is running in offline mode.'
107 | );
108 | });
109 | }
110 |
111 | export function unregister() {
112 | if ('serviceWorker' in navigator) {
113 | navigator.serviceWorker.ready.then(registration => {
114 | registration.unregister();
115 | });
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/src/util/Fetch.js:
--------------------------------------------------------------------------------
1 | const fetchData = async (url, { method = 'GET', headers = { 'Content-Type': 'application/json' }, body } = {}) => {
2 | try {
3 | return await fetch(url, Object.assign({
4 | method: method,
5 | headers: headers,
6 | },body?{body}:{}))
7 | .then((response) => {
8 | if (response.ok) {
9 | return response.json();
10 | }
11 | })
12 | .catch((err) => {
13 | console.warn(err);
14 | })
15 | } catch (error) {
16 | console.err(error)
17 | }
18 | }
19 |
20 | export default fetchData;
--------------------------------------------------------------------------------
/src/util/api.js:
--------------------------------------------------------------------------------
1 | const fetchData = async (url, { method = 'GET', headers = { 'Content-Type': 'application/json' }, body } = {}) => {
2 | try {
3 | return await fetch(url, Object.assign({
4 | method: method,
5 | headers: headers,
6 | }, body ? { body } : {}))
7 | .then((response) => {
8 | if (response.ok) {
9 | return response.json();
10 | }
11 | })
12 | .catch((err) => {
13 | console.warn(err);
14 | })
15 | } catch (error) {
16 | console.err(error)
17 | }
18 | }
19 |
20 | const url = 'https://api.github.com/graphql';
21 |
22 | const token = '2-7-4-b-0-1-7-e-5-f-a-7-9-b-b-9-b-7-b-b-9-8-8-5-8-6-2-4-7-7-7-7-7-6-f-3-7-c-f-e';
23 |
24 | const headers = {
25 | 'Content-Type': 'application/json',
26 | 'Authorization': `token ${token.replace(/-/g,'')}`
27 | }
28 |
29 | const getArticleList = async ({ pageSize = 5, after = null, before = null, labels=null }) => {
30 | return await fetch(url, {
31 | method: 'POST',
32 | headers: headers,
33 | body: JSON.stringify({
34 | query: `
35 | {
36 | repository(owner: "XboxYan", name: "notes") {
37 | issues(first: ${pageSize}, states: OPEN, orderBy: {field: UPDATED_AT, direction: DESC},after:${after},before:${before},labels:${labels}) {
38 | pageInfo {
39 | hasPreviousPage
40 | startCursor
41 | hasNextPage
42 | endCursor
43 | }
44 | totalCount
45 | nodes {
46 | number
47 | title
48 | id
49 | createdAt
50 | labels(first: 10) {
51 | nodes {
52 | color
53 | name
54 | id
55 | }
56 | }
57 | }
58 | }
59 | }
60 | }
61 | `
62 | })
63 | })
64 | .then((response) => {
65 | if (response.ok) {
66 | return response.json();
67 | }
68 | })
69 | .catch((err) => {
70 | console.warn(err);
71 | })
72 | }
73 |
74 | const getArticleDetail = async ({ number }) => {
75 | return await fetch(url, {
76 | method: 'POST',
77 | headers: headers,
78 | body: JSON.stringify({
79 | query: `
80 | {
81 | repository(owner: "XboxYan", name: "notes") {
82 | issue(number:${number}) {
83 | body
84 | number
85 | id
86 | title
87 | createdAt
88 | labels(first: 10) {
89 | nodes {
90 | color
91 | name
92 | id
93 | }
94 | }
95 | }
96 | }
97 | }
98 | `
99 | })
100 | })
101 | .then((response) => {
102 | if (response.ok) {
103 | return response.json();
104 | }
105 | })
106 | .catch((err) => {
107 | console.warn(err);
108 | })
109 | }
110 |
111 | const getCategory = async () => {
112 | return await fetch(url, {
113 | method: 'POST',
114 | headers: headers,
115 | body: JSON.stringify({
116 | query: `
117 | {
118 | repository(owner: "XboxYan", name: "notes") {
119 | labels(first: 99) {
120 | nodes{
121 | color
122 | description
123 | color
124 | id
125 | isDefault
126 | name
127 | }
128 | totalCount
129 | }
130 | }
131 | }
132 | `
133 | })
134 | })
135 | .then((response) => {
136 | if (response.ok) {
137 | return response.json();
138 | }
139 | })
140 | .catch((err) => {
141 | console.warn(err);
142 | })
143 | }
144 |
145 | export { getArticleList,getArticleDetail,getCategory };
--------------------------------------------------------------------------------
/src/util/evanyou.js:
--------------------------------------------------------------------------------
1 | export default function () {
2 |
3 | document.addEventListener('click', function (e) {
4 | //e.preventDefault()
5 | })
6 |
7 | var c = document.getElementById('evanyou-canvas'),
8 | x = c.getContext('2d'),
9 | pr = window.devicePixelRatio || 1,
10 | w = window.innerWidth,
11 | h = window.innerHeight,
12 | f = 90,
13 | q,
14 | m = Math,
15 | r = 0,
16 | u = m.PI*2,
17 | v = m.cos,
18 | z = m.random
19 | c.width = w*pr
20 | c.height = h*pr
21 | x.scale(pr, pr)
22 | x.globalAlpha = 0.6
23 | function i(){
24 | x.clearRect(0,0,w,h)
25 | q=[{x:0,y:h*.7+f},{x:0,y:h*.7-f}]
26 | while(q[1].xh||t<0) ? y(p) : t
45 | }
46 | document.onclick = i
47 | document.ontouchstart = i
48 | i()
49 | return i
50 | }
--------------------------------------------------------------------------------