17 |
18 | ## Env settings
19 |
20 | ### Use direnv
21 |
22 | * Copy `.envrc.example` to `.envrc`
23 |
24 | ### Fill out params
25 |
26 | Create your OAuth tokens at [Personal Access Tokens](https://github.com/settings/tokens), and set below.
27 |
28 | * `GH_READONLY_TOKEN`: (Required) To fetch issues.
29 | * Select scopes: Turn off all scopes. (will display as `public access`)
30 | * `GH_WRITE_TOKEN`: (Optional) To edit issues on blog site. Don't use in public!!
31 | * Select scopes: Only `public_repo` scope.
32 |
33 | Set target repository
34 |
35 | * `GH_REPO_OWNER`
36 | * `GH_REPO_NAME`
37 |
38 | ## Build Setup
39 |
40 | ```bash
41 | # install dependencies
42 | $ npm install # Or yarn install
43 |
44 | # serve with hot reload at localhost:3000
45 | $ npm run dev
46 |
47 | # build for production and launch server
48 | $ npm run build
49 | $ npm start
50 |
51 | # generate static project
52 | $ npm run generate
53 | ```
54 |
55 | For detailed explanation on how things work, checkout the [Nuxt.js docs](https://github.com/nuxt/nuxt.js).
56 |
--------------------------------------------------------------------------------
/store/index.js:
--------------------------------------------------------------------------------
1 | import getIssues from '~/apollo/queries/getIssues'
2 |
3 | export const state = () => ({
4 | repoOwner: '',
5 | repoName: '',
6 | fetchIssuePerPage: 20,
7 | totalCount: 0,
8 | nodes: [],
9 | pageInfo: {}
10 | })
11 |
12 | export const mutations = {
13 | setRepoOwner(state, repoOwner) {
14 | state.repoOwner = repoOwner
15 | },
16 | setRepoName(state, repoName) {
17 | state.repoName = repoName
18 | },
19 | clearIssues(state) {
20 | state.totalCount = 0
21 | state.nodes = []
22 | state.pageInfo = {}
23 | },
24 | setIssues(state, { totalCount, nodes, pageInfo, append }) {
25 | state.totalCount = totalCount
26 | state.nodes = append ? [...state.nodes, ...nodes] : nodes
27 | state.pageInfo = pageInfo
28 | },
29 | updateNode(state, node) {
30 | const i = state.nodes.findIndex((n) => n.id === node.id)
31 | if (i >= 0) {
32 | state.nodes = [
33 | ...state.nodes.slice(0, i),
34 | node,
35 | ...state.nodes.slice(i + 1)
36 | ]
37 | }
38 | }
39 | }
40 |
41 | export const actions = {
42 | async nuxtServerInit({ commit, state }, { app, env }) {
43 | const { GH_REPO_OWNER: repoOwner, GH_REPO_NAME: repoName } = env
44 |
45 | commit('setRepoOwner', repoOwner)
46 | commit('setRepoName', repoName)
47 |
48 | try {
49 | const { data } = await app.apolloProvider.defaultClient.query({
50 | query: getIssues,
51 | variables: {
52 | repoOwner,
53 | repoName,
54 | fetchIssuePerPage: state.fetchIssuePerPage
55 | }
56 | })
57 | commit('setIssues', data.repository.issues)
58 | } catch (err) {
59 | console.error(err)
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/nuxt.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | modules: [
3 | '@nuxtjs/apollo',
4 | ['@nuxtjs/google-analytics', { id: 'UA-3536169-21' }]
5 | ],
6 | plugins: [
7 | '~/plugins/github-api-v3.js',
8 | { src: '~/plugins/vue-notifications', ssr: false },
9 | '~/plugins/infinite-loading.js',
10 | '~/plugins/prism.js'
11 | ],
12 | apollo: {
13 | clientConfigs: {
14 | default: '~/apollo/client-configs/default.js'
15 | }
16 | },
17 | css: ['~/assets/css/base.scss'],
18 | env: {
19 | GH_READONLY_TOKEN: process.env.GH_READONLY_TOKEN,
20 | GH_WRITE_TOKEN: process.env.GH_WRITE_TOKEN,
21 | GH_REPO_OWNER: process.env.GH_REPO_OWNER,
22 | GH_REPO_NAME: process.env.GH_REPO_NAME
23 | },
24 | /*
25 | ** Headers of the page
26 | */
27 | head: {
28 | title: 'GitHub Issue as blog',
29 | meta: [
30 | { charset: 'utf-8' },
31 | { name: 'viewport', content: 'width=device-width, initial-scale=1' },
32 | {
33 | hid: 'description',
34 | name: 'description',
35 | content: 'GitHub Issue as blog'
36 | }
37 | ],
38 | link: [{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }]
39 | },
40 | /*
41 | ** Customize the progress bar color
42 | */
43 | loading: { color: '#3B8070' },
44 | /*
45 | ** Build configuration
46 | */
47 | build: {
48 | vendor: ['vue-notifications', 'luxon'],
49 | /*
50 | ** Run ESLint on save
51 | */
52 | extend(config, { isDev, isClient }) {
53 | if (isDev && isClient) {
54 | config.module.rules.push({
55 | enforce: 'pre',
56 | test: /\.(js|vue)$/,
57 | loader: 'eslint-loader',
58 | exclude: /(node_modules)/
59 | })
60 | }
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/assets/css/base.scss:
--------------------------------------------------------------------------------
1 | @import 'node_modules/modern-normalize/modern-normalize.css';
2 | @import 'node_modules/prismjs/themes/prism.css';
3 | @import '~assets/css/_vars.scss';
4 |
5 | @font-face {
6 | font-family: 'Yu Gothic';
7 | src: local('Yu Gothic Medium');
8 | font-weight: 100;
9 | }
10 | @font-face {
11 | font-family: 'Yu Gothic';
12 | src: local('Yu Gothic Medium');
13 | font-weight: 200;
14 | }
15 | @font-face {
16 | font-family: 'Yu Gothic';
17 | src: local('Yu Gothic Medium');
18 | font-weight: 300;
19 | }
20 | @font-face {
21 | font-family: 'Yu Gothic';
22 | src: local('Yu Gothic Medium');
23 | font-weight: 400;
24 | }
25 | @font-face {
26 | font-family: 'Yu Gothic';
27 | src: local('Yu Gothic Bold');
28 | font-weight: bold;
29 | }
30 | @font-face {
31 | font-family: 'Helvetica Neue';
32 | src: local('Helvetica Neue Regular');
33 | font-weight: 100;
34 | }
35 | @font-face {
36 | font-family: 'Helvetica Neue';
37 | src: local('Helvetica Neue Regular');
38 | font-weight: 200;
39 | }
40 |
41 | body {
42 | font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', YuGothic,
43 | '游ゴシック Medium', YuGothicM, Verdana, Meiryo, sans-serif;
44 | }
45 | @media all and (-ms-high-contrast: none) {
46 | body {
47 | font-family: Verdana, Meiryo, sans-serif;
48 | }
49 | }
50 | @media all and (-ms-high-contrast: active) {
51 | body {
52 | font-family: Verdana, Meiryo, sans-serif;
53 | }
54 | }
55 |
56 | body {
57 | font-size: 16px;
58 | background: $clr-1;
59 | color: $clr-b;
60 | }
61 |
62 | h1 {
63 | font-size: 1.6rem;
64 | }
65 | h2 {
66 | font-size: 1.4rem;
67 | }
68 | h3 {
69 | font-size: 1.2rem;
70 | }
71 | h4 {
72 | font-size: 1rem;
73 | }
74 | h5 {
75 | font-size: 1rem;
76 | }
77 | h6 {
78 | font-size: 1rem;
79 | }
80 | h1,
81 | h2,
82 | h3,
83 | h4,
84 | h5,
85 | h6 {
86 | margin: 0.5rem 0;
87 | font-weight: 700;
88 | color: $clr-b-d;
89 | text-shadow: 2px 2px 1px #fff, -2px 2px 1px #fff, 2px -2px 1px #fff,
90 | -2px -2px 1px #fff;
91 | }
92 |
93 | a {
94 | text-decoration: none;
95 | }
96 | .body a {
97 | color: $clr-1;
98 | text-shadow: 2px 2px 1px #fff, -2px 2px 1px #fff, 2px -2px 1px #fff,
99 | -2px -2px 1px #fff;
100 | word-break: break-all;
101 | }
102 |
103 | blockquote {
104 | padding: 0.5rem 1rem;
105 | margin: 1rem 0;
106 | border-left: 4px solid $clr-w;
107 | font-size: 0.9rem;
108 | color: $clr-b-l;
109 |
110 | & > :first-child {
111 | margin-top: 0;
112 | }
113 | & > :last-child {
114 | margin-bottom: 0;
115 | }
116 | }
117 |
118 | iframe {
119 | max-width: 100%;
120 | }
121 |
122 | .twitter-tweet-rendered {
123 | display: inline-block !important;
124 | }
125 |
126 | .marked {
127 | word-break: break-word;
128 |
129 | img {
130 | max-width: 100%;
131 | margin: 1rem 0;
132 | }
133 | }
134 | pre {
135 | padding: 1rem;
136 | overflow: auto;
137 | background: #f5f2f0;
138 | border-radius: 0.3rem;
139 | }
140 | code {
141 | border-radius: 0.3rem;
142 | padding: 0.3rem;
143 | margin: 0.3rem;
144 | overflow-x: scroll;
145 | background: #f5f2f0;
146 | color: black;
147 | }
148 |
--------------------------------------------------------------------------------
/pages/index.vue:
--------------------------------------------------------------------------------
1 |
2 |