-
-
Notifications
You must be signed in to change notification settings - Fork 162
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add support for Staticman comments (#725)
* feat: add support for Staticman comments * chore(staticman): replace baseURL with endpoint, and add the service parameter to use v3 API * i18n(staticman): add translations * feat(staticman): format comment date distance from now * chore: clean up * feat(staticman): add support for reCaptcha v2 * feat(staticman): add extra fields * fix(staticman): avoid submitting an invalid form * fix(staticman): filter invalid message * fix(staticman): verify reCaptcha * style(table): add gaps between responsive table and others * docs: document the Staticman comments engine * docs: update Staticman advantages and disadvantages * chore: update backers Add Wilson E. Alvarez as a backer * docs: update Staticman advantages * feat(staticman): paginate comments pages * docs: document the staticman.paginate parameter * feat(staticman): add the staticman.requiredFields parameter * chore(staticman): use the mystery-person image as the default avatar * feat(staticman): add the staticman.moderation parameter * docs: clean up * style: change the comment name color * chore: show comments pagination when total pages greater than 1 * fix(staticman): save comments to data folder * fix(staticman): correct comments pages title * fix(staticman): reset reCaptcha after submitting * fix(static): do not reset submit button after submitting when recaptcha is enabled * fix(staticman): do not disabled submit button if reCaptcha is disabled * chore(staticman): display the API error message if possible * docs: update Staticman pages * fix(staticman): adjust reCaptcha theme on first load * style(staticman): change fields width as full width on small screens * docs: fix requiredFields and empty siteKey for staticman.yml Co-authored-by: Wilson E. Alvarez <wilson.e.alvarez1@gmail.com>
- Loading branch information
Showing
37 changed files
with
1,281 additions
and
59 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
import { default as params } from '@params'; | ||
import snackbar from 'js/snackbar'; | ||
|
||
class Client | ||
{ | ||
private apiUrl: string; | ||
|
||
private reCaptchaKey = ''; | ||
private reCaptchaSecret = ''; | ||
|
||
private moderation: boolean; | ||
|
||
constructor() | ||
{ | ||
const endpoint = params.staticman.endpoint.replace(/\/*$/, "") | ||
const service = params.staticman.service ? params.staticman.service : 'github' | ||
const repo = params.staticman.repo | ||
const branch = params.staticman.branch ? params.staticman.branch : 'master' | ||
const property = params.staticman.property ? params.staticman.property : 'comments' | ||
this.reCaptchaKey = params.staticman.recaptchakey ? params.staticman.recaptchakey : ''; | ||
this.reCaptchaSecret = params.staticman.recaptchasecret ? params.staticman.recaptchasecret : ''; | ||
this.moderation = "moderation" in params.staticman ? params.staticman.moderation : true; | ||
this.apiUrl = `${endpoint}/v3/entry/${service}/${repo}/${branch}/${property}` | ||
} | ||
|
||
send(form: FormData) { | ||
const reCaptchaToken = form.get('reCaptchaToken') | ||
if (this.reCaptchaKey && !reCaptchaToken) { | ||
throw new Error('reCaptcha token missing') | ||
} | ||
|
||
let slug = form.get('slug') | ||
|
||
const data = { | ||
'g-recaptcha-response': reCaptchaToken, | ||
options: { | ||
slug: slug, | ||
reCaptcha: { | ||
siteKey: this.reCaptchaKey, | ||
secret: this.reCaptchaSecret, | ||
}, | ||
}, | ||
fields: { | ||
reply_to: form.get('reply_to'), | ||
root_id: form.get('root_id'), | ||
name: form.get('name'), | ||
email: form.get('email'), | ||
message: form.get('message'), | ||
url: form.get('url'), | ||
}, | ||
} | ||
|
||
return fetch(this.apiUrl, { | ||
method: 'POST', | ||
headers: { | ||
'Content-Type': 'application/json' | ||
}, | ||
body: JSON.stringify(data), | ||
}).then((response) => { | ||
return response.json(); | ||
}).then((data) => { | ||
if (data.success === true) { | ||
snackbar.show(this.sucessMessage(), 5000); | ||
} else if (data.message) { | ||
snackbar.show(data.message); | ||
} else { | ||
snackbar.show(data.error.text); | ||
} | ||
}).catch((err) => { | ||
console.error(err) | ||
snackbar.show('Comment failed.'); | ||
}) | ||
} | ||
|
||
sucessMessage(): string | ||
{ | ||
if (this.moderation) { | ||
return 'Comment successfully submitted! Your message will be shown once our moderators review it.'; | ||
} | ||
|
||
return 'Comment successfully submitted! Your message will be shown in a few minutes.'; | ||
} | ||
} | ||
|
||
const client = new Client(); | ||
|
||
export default client; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
import { formatDistance } from 'date-fns'; | ||
import { enUS, zhCN, zhTW } from 'date-fns/locale'; | ||
|
||
const locales = { | ||
'en': enUS, | ||
'en-us': enUS, | ||
'zh-cn': zhCN, | ||
'zh-hans': zhCN, | ||
'zh-tw': zhTW, | ||
'zh-hk': zhTW, | ||
'zh-hant': zhTW, | ||
}; | ||
|
||
class DateRenderer | ||
{ | ||
private items: Array<HTMLElement>; | ||
private lang: string; | ||
|
||
constructor(items: string) | ||
{ | ||
this.items = Array.from(document.querySelectorAll(items)) as Array<HTMLElement> | ||
this.lang = document.documentElement.getAttribute('lang').toLowerCase(); | ||
} | ||
|
||
run() | ||
{ | ||
if (!this.items) { | ||
return | ||
} | ||
|
||
const now = new Date(); | ||
this.items.forEach((item: HTMLElement) => { | ||
const timestamp = parseInt(item.getAttribute('data-timestamp')); | ||
const date = new Date(timestamp * 1000); | ||
item.innerText = formatDistance(now, date, { addSuffix: true, locale: this.getLocale() }) | ||
}) | ||
} | ||
|
||
getLocale() | ||
{ | ||
return this.lang in locales ? locales[this.lang] : enUS; | ||
} | ||
} | ||
|
||
export default DateRenderer; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
import Component from "js/component"; | ||
|
||
abstract class Form implements Component | ||
{ | ||
protected form: HTMLFormElement; | ||
|
||
private btnSubmit: HTMLButtonElement; | ||
|
||
private locked = false; | ||
|
||
constructor(form: string) | ||
{ | ||
this.form = document.getElementById(form) as HTMLFormElement | ||
} | ||
|
||
run() { | ||
if (!this.form) { | ||
return; | ||
} | ||
|
||
this.btnSubmit = this.form.querySelector('button[type="submit"]'); | ||
|
||
this.form.addEventListener('submit', (e) => { | ||
e.preventDefault() | ||
if (!this.form.checkValidity()) { | ||
e.stopPropagation() | ||
return; | ||
} | ||
|
||
if (this.locked !== false) { | ||
return; | ||
} | ||
|
||
this.lock(); | ||
|
||
this.submit(new FormData(this.form)).finally(() => { | ||
this.unlock(); | ||
this.reset(); | ||
}); | ||
}) | ||
} | ||
|
||
abstract submit(data: FormData): Promise<void>; | ||
|
||
lock() { | ||
this.locked = true | ||
this.btnSubmit.setAttribute('disabled', 'true'); | ||
} | ||
|
||
unlock() { | ||
this.locked = false | ||
if (!this.form.hasAttribute('data-g-recaptcha-id')) { | ||
this.btnSubmit.removeAttribute('disabled'); | ||
} | ||
} | ||
|
||
reset() { | ||
this.form.reset(); | ||
|
||
const reCaptchaWidgetId = this.form.getAttribute('data-g-recaptcha-id'); | ||
if (reCaptchaWidgetId) { | ||
window.grecaptcha.reset(reCaptchaWidgetId) | ||
} | ||
} | ||
} | ||
|
||
export default Form; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import client from "js/staticman/client"; | ||
import DateRenderer from "js/staticman/date-renderer"; | ||
import Form from "js/staticman/form"; | ||
import Reply from "js/staticman/reply"; | ||
|
||
class Staticman extends Form | ||
{ | ||
submit(data: FormData): Promise<void> | ||
{ | ||
return client.send(data); | ||
} | ||
} | ||
|
||
(new Staticman('staticman-comment-form')).run(); | ||
|
||
(new Reply('staticman-reply-form', '.staticman-reply-button')).run(); | ||
|
||
(new DateRenderer('.staticman-comment-date')).run(); |
Oops, something went wrong.