Permalink
Browse files

Add content

  • Loading branch information...
tatthien committed Dec 26, 2018
1 parent 5bef688 commit a4b6b501fde2d8e57971d3a26ebc205699a25400
Showing with 1,351 additions and 1 deletion.
  1. +6 −0 .editorconfig
  2. +4 −0 .gitignore
  3. +1 −1 README.md
  4. +6 −0 archetypes/default.md
  5. +5 −0 archetypes/til.md
  6. +31 −0 config.toml
  7. +7 −0 content/pages/about.md
  8. +104 −0 content/posts/xu-li-anh.md
  9. +30 −0 netlify.toml
  10. +28 −0 resources/_gen/assets/js/js/app.js_d11fe7b62c27961c87ecd0f2490357b9.content
  11. +1 −0 resources/_gen/assets/js/js/app.js_d11fe7b62c27961c87ecd0f2490357b9.json
  12. +34 −0 resources/_gen/assets/js/js/comment.js_d11fe7b62c27961c87ecd0f2490357b9.content
  13. +1 −0 resources/_gen/assets/js/js/comment.js_d11fe7b62c27961c87ecd0f2490357b9.json
  14. +1 −0 resources/_gen/assets/js/js/highlight.pack.js_d11fe7b62c27961c87ecd0f2490357b9.content
  15. +1 −0 resources/_gen/assets/js/js/highlight.pack.js_d11fe7b62c27961c87ecd0f2490357b9.json
  16. +1 −0 resources/_gen/assets/scss/scss/style.scss_48b060fe05b0a273d182ef83c0605941.content
  17. +1 −0 resources/_gen/assets/scss/scss/style.scss_48b060fe05b0a273d182ef83c0605941.json
  18. +2 −0 themes/blog/archetypes/default.md
  19. +99 −0 themes/blog/assets/css/github.css
  20. +94 −0 themes/blog/assets/js/app.js
  21. +96 −0 themes/blog/assets/js/comment.js
  22. +2 −0 themes/blog/assets/js/highlight.pack.js
  23. +77 −0 themes/blog/assets/scss/_comment.scss
  24. +21 −0 themes/blog/assets/scss/_mixins.scss
  25. +13 −0 themes/blog/assets/scss/_variables.scss
  26. +388 −0 themes/blog/assets/scss/style.scss
  27. 0 themes/blog/layouts/404.html
  28. +9 −0 themes/blog/layouts/_default/list.algolia.json
  29. +15 −0 themes/blog/layouts/_default/list.html
  30. +12 −0 themes/blog/layouts/_default/single.html
  31. +17 −0 themes/blog/layouts/index.html
  32. +45 −0 themes/blog/layouts/partials/comment.html
  33. +23 −0 themes/blog/layouts/partials/footer.html
  34. +127 −0 themes/blog/layouts/partials/header.html
  35. +26 −0 themes/blog/layouts/partials/seo_schema.html
  36. BIN themes/blog/static/img/ibm-typeface-ibm-plex-01.jpeg
  37. BIN themes/blog/static/img/ibm-typeface-ibm-plex-02.jpeg
  38. BIN themes/blog/static/img/ibm-typeface-ibm-plex-03.jpeg
  39. BIN themes/blog/static/img/ibm-typeface-ibm-plex-04.jpeg
  40. BIN themes/blog/static/img/icon/default-avatar.png
  41. BIN themes/blog/static/img/icon/favicon.png
  42. BIN themes/blog/static/img/the-humanity-behind-a-slack-bot-01.png
  43. BIN themes/blog/static/img/the-humanity-behind-a-slack-bot-02.png
  44. +2 −0 themes/blog/static/js/md5.min.js
  45. +21 −0 themes/blog/theme.toml
@@ -0,0 +1,6 @@
[*]
indent_style = space
indent_size = 2
trim_trailing_whitespace = true
insert_final_newline = true
end_of_line = lf
@@ -0,0 +1,4 @@
public/
.env
node_modules/
.DS_Store
@@ -1 +1 @@
12bit.vn
# notes-storage
@@ -0,0 +1,6 @@
---
title: "{{ replace .Name "-" " " | title }}"
date: {{ .Date }}
draft: true
---

@@ -0,0 +1,5 @@
---
title: "{{ .Name }}"
date: {{ .Date }}
---

@@ -0,0 +1,31 @@
baseURL = "https://note.tatthien.com"
languageCode = "vi-vn"
title = "12bit.vn"
theme = "blog"
enableGitInfo = true
disqusShortname = "shortname"
enableEmoji = true
enableRobotsTXT = true
googleAnalytics = "UA-126210212-1"
uglyURLs=true
paginate=20


[outputFormats.Algolia]
baseName = "algolia"
isPlainText = true
mediaType = "application/json"
notAlternative = true

[params.algolia]
vars = ["title", "content", "summary", "date", "publishdate", "permalink"]
params = ["tags"]

[outputs]
home = ["HTML", "RSS", "Algolia"]

[params]
description = "write and share to learn"
favicon = "img/icon/favicon.png"
[blackfriday]
hrefTargetBlank = true
@@ -0,0 +1,7 @@
---
title: "About"
date: 2018-09-21T11:06:27+07:00
draft: false
---

This site is "private".
@@ -0,0 +1,104 @@
---
title: "Xử lý khi không load được ảnh bằng cách sử dụng service worker"
date: 2018-12-26T09:36:03+07:00
draft: false
---

Có nhiều lý đo dể một website không load được ảnh, ví dụ như file ảnh đã bị xóa, ảnh quá nặng và bị timeout, … Trong những trường hợp đó thì tùy browser mà lỗi sẽ hiển thị khác nhau, có thể xấu có thể đẹp.

Bạn có handle nó hay không thì cũng không có gì lớn lao lắm, nhưng nếu có thời gian và công sức thì cứ handle cho vui và tập cái tính cẩn thận và hiện thực cái câu “deliver more than expected”. Nhưng đừng dại làm những việc nay khi project đang trong lúc nước sôi lửa bỏng nhé.

Năm nay chúng ta đã nghe khác nhiều về service worker rồi, là trái tim của PWA. Nó cho phép script của bạn chạy trong một thread riêng biệt so với thread chính, theo dõi và cang thiệp vào các network request của trang web. Bạn có thể hiện thực cache, hoặc push message bằng service worker.

Nhưng đợi đã, nếu bạn có thể can thiệp vào network request, thì bạn sẽ biết được browser đang request cái gì và có thành công hay không, bạn cũng có thể return lại một result khác cho browser. Vậy tại sao lại không dùng nó để:

1. Kiểm tra xem có phải đang request ảnh hay không
2. Kiểm tra xem kết quả thành công hay không
3. Nếu không thành công thì return về một tấm ảnh placeholder nào đó.

## Register service worker

Bạn phải đăng ký với browser file mà bạn sẽ dùng để chạy như là một service worker, việc này được thực hiệnbằng JavaScript. Giả sự bạn có file main.js được load trong trang, và bạn sẽ cần viết code đăng ký file service worker trong file đó như sau:

```js
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/service-worker.js')
.then(function(registration) {
console.log('Registration successful, scope is:', registration.scope);
})
.catch(function(error) {
console.log('Service worker registration failed, error:', error);
});
}
```

Trong ví dụ này, file service worker của chúng ta có tên là service-worker.js. Bạn lưu ý một vài yêu cầu như sau:

1. File service-worker.js phải nằm ở root level, không được nằm trong thư mục.
2. Website phải sử dụng SSL (HTTPS)

## Handle fetch event

Service work hoạt động dựa vào event, nghĩa là browser sẽ gọi các event, bạn lắng nghe và hồi đáp.

Event fetch được gọi khi browser cần request resource. Chúng ta sẽ lắng nghe event này file trong file service-worker.js:

```js
self.addEventListener('fetch', (e) => {
e.respondWith(
fetch(e.request)
.then((response) => {
if (response.ok) return response;
// User is online, but response was not ok
})
.catch((err) => {
// User is probably offline
})
)
});
```

Thuộc tính ok cho chúng ta biết việc request này có nhận được kết quả ok hay không. Nếu nó ok, thì không có gì phải nói, cứ return lại response là được, trong trường hợp không ok thì request tới một tấm ảnh khác và return lại response:

```js
function isImage (fetchRequest) {
return fetchRequest.method === 'GET' && fetchRequest.destination === 'image'
}
self.addEventListener('fetch', (e) => {
e.respondWith(
fetch(e.request)
.then((response) => {
if (response.ok) return response
if (!isImage(e.request)) return response
return fetch('/broken.png')
})
.catch((err) => {
if (isImage(e.request)) {
return fetch('/broken.png')
}
})
)
})
```

## Precache image

Chúng ta không thể cứ mãi fetch lên server để lấy file broken.png mãi đúng không, chúng ta đang ở trong service worker, và chúng ta có quyền cache. Có thể cache file này khi service worker được install, sau đó lấy từ trong cache ra để hồi đáp lại browser:

```js
self.addEventListener('install', (e) => {
self.skipWaiting();
e.waitUntil(
caches.open("precache").then((cache) => {
// Add /broken.png to "precache"
cache.add("/broken.png");
})
);
});
```

Giờ quay lại event fetch và thay fetch('/broken.png') bằng caches.match('/broken.png')

Đoạn code đầy đủ sẽ như sau:
@@ -0,0 +1,30 @@
[build]
publish = "public"
command = "hugo"

[context.production.environment]
HUGO_VERSION = "0.46"
HUGO_ENV = "production"
HUGO_ENABLEGITINFO = "true"

[context.split1]
command = "hugo --enableGitInfo"

[context.split1.environment]
HUGO_VERSION = "0.46"
HUGO_ENV = "production"

[context.deploy-preview]
command = "hugo --buildFuture -b $DEPLOY_PRIME_URL"

[context.deploy-preview.environment]
HUGO_VERSION = "0.46"

[context.branch-deploy]
command = "hugo -b $DEPLOY_PRIME_URL"

[context.branch-deploy.environment]
HUGO_VERSION = "0.46"

[context.next.environment]
HUGO_ENABLEGITINFO = "true"
@@ -0,0 +1,28 @@
WebFont.load({google:{families:['IBM Plex Mono:500','IBM Plex Sans:400,400i,500,500i&subset=vietnamese']}});const resultPanel=document.getElementById('hits')
const search=instantsearch({appId:'P3HLF1KE43',apiKey:'f4276c1eacc7a75de89e5352d42a070a',indexName:'prod_TIL_12BITVN',routing:true,searchParameters:{hitsPerPage:5},searchFunction(helper){if(helper.state.query.trim()!==''){helper.search()}}});search.addWidget(instantsearch.widgets.searchBox({container:'#search-box',placeholder:'Search',reset:false,autofocus:false}));const itemTemplate=`
<a class="search-result-item-{{ type }} search-result-item" href="{{ relpermalink }}">
<h3 class="title">{{ title }}</h3>
{{ #summary }}
<p class="summary">{{{_highlightResult.summary.value}}}</p>
{{ /summary }}
</a>
`
search.addWidget(instantsearch.widgets.hits({container:resultPanel,templates:{empty:'<span class="na">No results</span>',item:itemTemplate}}))
const algoliaLogo=document.createElement('img')
algoliaLogo.src='https://www.algolia.com/static_assets/images/pricing/pricing_new/algolia-powered-by-14773f38.svg'
algoliaLogo.classList.add('algolia-logo')
search.on('render',()=>{resultPanel.appendChild(algoliaLogo)
let itemTitle=document.querySelectorAll('#hits .title')
itemTitle.forEach((item)=>{let text=item.textContent
let date=text.split('-')
if(date.length!==3){return}
let d=date[2]
let m=date[1]
let y=date[0]
item.textContent=`${d}/${m}/${y}`})})
search.start();const searchBox=document.querySelector('#search-box input')
const hideResultPanel=()=>resultPanel.style.display='none'
const showResultPanel=()=>resultPanel.style.display='block'
document.addEventListener('click',(e)=>{if(!resultPanel.contains(e.target)&&e.target!==searchBox){hideResultPanel()}})
searchBox.addEventListener('focus',hideResultPanel)
searchBox.addEventListener('input',(e)=>{searchBox.value.trim()===''?hideResultPanel():showResultPanel()})
@@ -0,0 +1 @@
{"Target":"js/app.min.eab8f83dfb4b802f4c0a60837c9f9a79bdf746ca7e82ce1a08b5043d26b7b921.js","MediaType":"application/javascript","Data":{"Integrity":"sha256-6rj4PftLgC9MCmCDfJ+aeb33Rsp+gs4aCLUEPSa3uSE="}}
@@ -0,0 +1,34 @@
var app=new Vue({el:'#js_comment',data:{isAnonymous:false,isFocusOnContent:false,content:'',author:'',email:'',slug:'',comments:[],graphqlEndpoint:'https://api.graph.cool/simple/v1/cjmgeu7349fgx0199m25vmn1w'},async mounted(){this.slug=this.$el.dataset.slug
let data=await this.getComments()
this.comments=data.allComments},methods:{async getComments(){let query=`query {allComments(filter: {
slug: "${this.slug}"
}) {id, author, email, content, createdAt, slug}}`
let data=await this.request(query)
return data},async postComment(){if(this.content===''){return}
if(!this.isAnonymous&&this.author===''){return}
this.author=this.isAnonymous?'Anonymous':this.author
let query=`mutation {
createComment(
author: "${this.author}"
email: "${this.email}"
content: "${this.content}"
slug: "${this.slug}"
) {
id
content
author
email
slug
createdAt
}
}`
let data=await this.request(query)
this.comments.push(data.createComment)
this.cleanForm()},async request(query,method='GET'){let res=await fetch(this.graphqlEndpoint,{method:'POST',mode:'cors',headers:{'Content-Type':'application/json','Accept':'application/json',},body:JSON.stringify({query})})
let json=await res.json()
return json.data},cleanForm(){this.content=''},formatDate(dateStr){let monthNames=["January","February","March","April","May","June","July","August","September","October","November","December"];let date=new Date(dateStr)
let day=date.getDate()
let monthIndex=date.getMonth()
let year=date.getFullYear()
return monthNames[monthIndex]+' '+day+', '+year}},filters:{avatar(email){if(email===''){return '/img/icon/default-avatar.png';}
return `https://www.gravatar.com/avatar/${md5(email)}?s=60`;}}})
@@ -0,0 +1 @@
{"Target":"js/comment.min.41de13e77d2abd2f5dbab2029e1b36bec2d0e15750db66dbf7adf20d85e6dc7c.js","MediaType":"application/javascript","Data":{"Integrity":"sha256-Qd4T530qvS9durICnhs2vsLQ4VdQ22bb963yDYXm3Hw="}}

Large diffs are not rendered by default.

Oops, something went wrong.
@@ -0,0 +1 @@
{"Target":"js/highlight.pack.min.66cc071083039771b2d396afb37d68ca60912661b857761bfb30634906c31c6b.js","MediaType":"application/javascript","Data":{"Integrity":"sha256-ZswHEIMDl3Gy05avs31oymCRJmG4V3Yb+zBjSQbDHGs="}}
@@ -0,0 +1 @@
.comment{}.comment__title{font-weight:500;font-size:19.2px;font-size:1.2rem}.comment__form{margin-bottom:2rem}.comment__form-input{margin-bottom:1rem}.comment__form-input label{display:block;font-size:16px;font-size:1rem}.comment__form-input input,.comment__form-input textarea{width:100%;border-radius:4px;border:2px solid #2c2c2c33;padding:10px;outline:none;font-size:16px;font-size:1rem}.comment__form-input input:focus,.comment__form-input textarea:focus{border-color:#fa7268}.comment__form-input textarea{height:100px}.comment__form-input input[type=checkbox],.comment__form-input input[type=radio]{width:auto}.comment__form-input input[type=submit]{background-color:#fa7268;border:none;color:#fff;font-weight:500;width:auto;padding:10px 15px;cursor:pointer}.comment__form-input input[type=submit]:hover{opacity:.8}.comment__item{display:flex;align-items:flex-start;margin:1.5rem 0;font-size:16px;font-size:1rem}.comment__item .avatar{margin-right:1rem;width:60px;height:60px}.comment__item .avatar img{border-radius:50%;width:100%;height:auto}.comment__item .author{margin:0;padding:0}.comment__item .author time{font-weight:400;margin-left:1rem;color:#2c2c2c80}.comment__item .content{margin-top:10px}body{font-family:ibm plex sans,sans-serif;font-size:18px;line-height:1.6;font-weight:400;font-display:auto;color:#2c2c2c}h1,h2,h3,h4,h5,h6{font-family:ibm plex sans,sans-serif;font-weight:500}a{text-decoration:none;color:#2c2c2c80}a:hover{color:#2c2c2c}a[target=_blank]:after{content:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGNsYXNzPSJpLWV4dGVybmFsIiB2aWV3Qm94PSIwIDAgMzIgMzIiIHdpZHRoPSIxNCIgaGVpZ2h0PSIxNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJyZ2JhKDQ0LCA0NCwgNDQsIDAuNSkiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLXdpZHRoPSI5LjM4JSI+PHBhdGggZD0iTTE0IDlIM1YyOUgyM1YxOE0xOCA0SDI4VjE0TTI4IDQgMTQgMTgiLz48L3N2Zz4=);margin-left:5px;position:relative;top:-2px}.heart{color:red}#wrap{max-width:750px;margin:0 auto;position:relative;padding-left:15px;padding-right:15px}@media(min-width:768px){#wrap{padding-left:25px;padding-right:25px}}.site-header{display:flex;justify-content:space-between;align-items:center;padding:1rem 0;flex-flow:column}@media(min-width:768px){.site-header{padding:2rem 0;flex-flow:row}}.site-header .left{text-align:center}@media(min-width:768px){.site-header .left{text-align:left}}.site-header .site-title{font-size:32px;font-size:2rem;margin:10px 0}@media(min-width:768px){.site-header .site-title{margin:0;font-size:48px;font-size:3rem}}.site-header .site-title a{color:#2c2c2c}.site-header .site-title sup{color:#fff;top:-1.5rem;padding:0 7px;border-radius:50px;font-size:16px;font-size:1rem}.site-header .site-desc{font-weight:400;margin:0}.site-header .site-navigation ul{list-style:none;margin:0;padding:0;display:flex;justify-content:center;align-items:center}.site-header .site-navigation ul a{display:block}.site-header .site-navigation__item{padding-left:20px}.site-header .site-navigation__item:first-child{padding-left:0}.site-header .site-navigation .cta{border:2px solid #fa7268;color:#fa7268;border-radius:4px;padding:5px 15px;font-weight:500}.site-header .site-navigation .cta:hover{color:#fff;background-color:#fa7268}.article{margin:2rem 0;padding-bottom:2rem;border-bottom:1px solid #2c2c2c1a}.article:first-child{margin-top:0}.article:last-child{border:none}.article__title{font-size:32px;font-size:2rem;margin-bottom:0;font-weight:500;color:#fa7268}.article__title a{color:#fa7268}.article__intro{color:#2c2c2c80}.article__date{color:#2c2c2c80;font-size:12.8px;font-size:.8rem}.article__content p{margin:1rem 0}.article__content ol li,.article__content ul li{margin-bottom:10px;position:relative}.article__content ol{list-style:numeric;padding-left:30px}.article__content ul{list-style:none;padding:0}.article__content ul li{padding-left:15px}.article__content ul li:before{content:'-';position:absolute;left:0}.article__content strong{font-weight:500}.article__content em{font-style:italic}.article__content code,.article__content pre{font-family:ibm plex mono;font-size:14px;background-color:#2c2c2c14;padding:0 10px 3px;border-radius:4px}.article__content pre{padding:0}.article__content pre code{background-color:#0000}.article__content a{color:#fa7268}.article__content a:hover{background-color:#fa7268;color:#fff}.article__content h1:before,.article__content h2:before{color:#2c2c2c80;margin-right:5px}.article__content h1:before{content:'#'}.article__content h2:before{content:'##'}.article__content img{max-width:100%;height:auto}.article__content blockquote{font-size:24px;font-size:1.5rem;font-style:italic;border:2px dashed #2c2c2c1a;border-radius:4px;margin:1rem 0;padding:1rem}.article__content figure{padding:0;margin:1rem 0}.article__content figure a{font-size:0}.article__content figure a:after{display:none}.article--til .article__content ul,.article--til .article__content ol{list-style:none;padding:0}.article--til .article__content ul li,.article--til .article__content ol li{position:relative}.article--til .article__content ul li:before,.article--til .article__content ol li:before{content:'-';position:absolute;left:0}#colophon{padding:2rem 0;margin-top:4rem;font-family:ibm plex sans,sans-serif;border-top:1px solid #2c2c2c80;text-align:center}#colophon p{margin:0}#colophon .main-domain{color:#fa7268;font-weight:500}.pagination{list-style:none;margin:0;padding:0;display:flex;justify-content:center;align-items:center}.pagination .page-item a{padding:10px}.pagination .page-item.active{border:2px solid #fa7268;border-radius:4px}.pagination .page-item.active a{color:#fa7268;font-weight:500}#algolia{margin-bottom:2.5rem}#search-box .ais-search-box{width:100%;max-width:100%}#search-box .ais-search-box--input{border-radius:50px;border:2px solid #2c2c2c26;padding:10px 15px 10px 45px;outline:none;box-shadow:none}#search-box .ais-search-box--input:focus{border-color:#fa7268}#search-box .ais-search-box--reset{top:15px;right:15px}#search-box .ais-search-box--magnifier{top:10px;left:18px}#search-box .ais-search-box--magnifier svg{height:22px;width:22px;fill:#2c2c2c26}#hits{background-color:#fff;box-shadow:0 2px 4px 0 #0000001a;border-radius:4px;overflow:hidden}#hits .algolia-logo{padding:20px;float:right}#hits .na{padding:20px;display:block}.ais-hits--item:first-child a{border-radius:4px 4px 0 0}.ais-hits--item:last-child a{border-bottom:none}.ais-hits--item a{color:#2c2c2c;display:block;padding:15px;border-bottom:1px solid #2c2c2c0d}.ais-hits--item a:hover{background:#fa72681a}.ais-hits--item h3{font-weight:400;margin-top:0;margin-bottom:10px}.ais-hits--item p{margin:0;font-size:.9rem}.ais-hits--item p em{background-color:#fa72684d;font-style:normal;font-weight:500}
@@ -0,0 +1 @@
{"Target":"scss/style.min.194568487552309bff9c8a6a1596d04532076a4804426e2ee0da3e9cd5080d67.css","MediaType":"text/css","Data":{"Integrity":"sha256-GUVoSHVSMJv/nIpqFZbQRTIHakgEQm4u4No+nNUIDWc="}}
@@ -0,0 +1,2 @@
+++
+++
@@ -0,0 +1,99 @@
/*
github.com style (c) Vasily Polovnyov <vast@whiteants.net>
*/

.hljs {
display: block;
overflow-x: auto;
padding: 0.5em;
color: #333;
background: #f8f8f8;
}

.hljs-comment,
.hljs-quote {
color: #998;
font-style: italic;
}

.hljs-keyword,
.hljs-selector-tag,
.hljs-subst {
color: #333;
font-weight: bold;
}

.hljs-number,
.hljs-literal,
.hljs-variable,
.hljs-template-variable,
.hljs-tag .hljs-attr {
color: #008080;
}

.hljs-string,
.hljs-doctag {
color: #d14;
}

.hljs-title,
.hljs-section,
.hljs-selector-id {
color: #900;
font-weight: bold;
}

.hljs-subst {
font-weight: normal;
}

.hljs-type,
.hljs-class .hljs-title {
color: #458;
font-weight: bold;
}

.hljs-tag,
.hljs-name,
.hljs-attribute {
color: #000080;
font-weight: normal;
}

.hljs-regexp,
.hljs-link {
color: #009926;
}

.hljs-symbol,
.hljs-bullet {
color: #990073;
}

.hljs-built_in,
.hljs-builtin-name {
color: #0086b3;
}

.hljs-meta {
color: #999;
font-weight: bold;
}

.hljs-deletion {
background: #fdd;
}

.hljs-addition {
background: #dfd;
}

.hljs-emphasis {
font-style: italic;
}

.hljs-strong {
font-weight: bold;
}
Oops, something went wrong.

0 comments on commit a4b6b50

Please sign in to comment.