artalk 2.3.3 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Artalk.css +1 -1
- package/dist/Artalk.es.js +3184 -2888
- package/dist/Artalk.iife.js +1 -1
- package/dist/Artalk.js +1 -1
- package/dist/ArtalkLite.css +1 -1
- package/dist/ArtalkLite.es.js +3157 -2887
- package/dist/ArtalkLite.iife.js +1 -1
- package/dist/ArtalkLite.js +1 -1
- package/dist/src/api/admin.d.ts +10 -0
- package/dist/src/api/api-base.d.ts +14 -0
- package/dist/src/api/captcha.d.ts +14 -0
- package/dist/src/api/comment.d.ts +29 -0
- package/dist/src/api/index.d.ts +31 -0
- package/dist/src/api/page.d.ts +22 -0
- package/dist/src/api/request.d.ts +12 -0
- package/dist/src/api/site.d.ts +17 -0
- package/dist/src/api/system.d.ts +13 -0
- package/dist/src/api/upload.d.ts +11 -0
- package/dist/src/api/user.d.ts +28 -0
- package/dist/src/artalk.d.ts +56 -0
- package/dist/src/comment/actions.d.ts +17 -0
- package/dist/src/comment/comment.d.ts +67 -0
- package/dist/src/comment/index.d.ts +2 -0
- package/dist/src/comment/render.d.ts +76 -0
- package/dist/src/components/action-btn.d.ts +52 -0
- package/dist/src/components/dialog.d.ts +18 -0
- package/dist/src/components/pagination.d.ts +30 -0
- package/dist/src/components/read-more-btn.d.ts +33 -0
- package/dist/src/context.d.ts +72 -0
- package/dist/src/defaults.d.ts +3 -0
- package/dist/src/editor/editor.d.ts +84 -0
- package/dist/src/editor/index.d.ts +2 -0
- package/dist/src/editor/plugs/editor-plug.d.ts +26 -0
- package/dist/src/editor/plugs/emoticons-plug.d.ts +36 -0
- package/dist/src/editor/plugs/header-input-plug.d.ts +13 -0
- package/dist/src/editor/plugs/preview-plug.d.ts +10 -0
- package/dist/src/editor/plugs/upload-plug.d.ts +10 -0
- package/dist/src/i18n/en.d.ts +69 -0
- package/dist/src/i18n/index.d.ts +294 -0
- package/dist/src/i18n/zh-CN.d.ts +3 -0
- package/dist/src/layer/index.d.ts +3 -0
- package/dist/src/layer/layer.d.ts +32 -0
- package/dist/src/layer/sidebar-layer.d.ts +20 -0
- package/dist/src/lib/checker/admin-checker.d.ts +3 -0
- package/dist/src/lib/checker/captcha-checker.d.ts +3 -0
- package/dist/src/lib/checker/index.d.ts +42 -0
- package/dist/src/lib/component.d.ts +12 -0
- package/dist/src/lib/detect.d.ts +10 -0
- package/dist/src/lib/stat.d.ts +15 -0
- package/dist/src/lib/ui.d.ts +23 -0
- package/dist/src/lib/user.d.ts +13 -0
- package/dist/src/lib/utils.d.ts +39 -0
- package/dist/src/list/index.d.ts +2 -0
- package/dist/src/list/list-lite.d.ts +63 -0
- package/dist/src/list/list-nest.d.ts +10 -0
- package/dist/src/list/list.d.ts +26 -0
- package/dist/src/main.d.ts +4 -0
- package/dist/types/artalk-config.d.ts +132 -0
- package/dist/types/artalk-data.d.ts +204 -0
- package/dist/types/context.d.ts +146 -0
- package/dist/types/event.d.ts +28 -0
- package/dist/types/global.d.ts +0 -0
- package/dist/types/plug.d.ts +4 -0
- package/index.html +3 -3
- package/package.json +5 -5
- package/src/api/admin.ts +18 -0
- package/src/api/api-base.ts +27 -0
- package/src/api/captcha.ts +24 -0
- package/src/api/comment.ts +84 -0
- package/src/api/index.ts +25 -361
- package/src/api/page.ts +85 -0
- package/src/api/request.ts +12 -8
- package/src/api/site.ts +51 -0
- package/src/api/system.ts +39 -0
- package/src/api/upload.ts +29 -0
- package/src/api/user.ts +75 -0
- package/src/artalk.ts +75 -76
- package/src/comment/actions.ts +3 -3
- package/src/comment/comment.ts +6 -9
- package/src/comment/render.ts +93 -47
- package/src/components/pagination.ts +0 -1
- package/src/context.ts +57 -18
- package/src/editor/editor.ts +111 -18
- package/src/editor/plugs/header-input-plug.ts +1 -1
- package/src/editor/plugs/upload-plug.ts +1 -1
- package/src/i18n/en.ts +3 -0
- package/src/i18n/index.ts +117 -2
- package/src/i18n/zh-CN.ts +4 -1
- package/src/layer/sidebar-layer.ts +1 -3
- package/src/lib/checker/admin-checker.ts +1 -1
- package/src/lib/checker/captcha-checker.ts +3 -3
- package/src/lib/stat.ts +2 -2
- package/src/lib/ui.ts +6 -0
- package/src/list/list-lite.ts +33 -25
- package/src/list/list.ts +3 -4
- package/src/main.ts +3 -0
- package/src/style/comment.less +2 -3
- package/src/style/main.less +6 -0
- package/test/CSRF.html +26 -0
- package/types/artalk-data.d.ts +3 -0
- package/types/context.d.ts +19 -1
- package/types/global.d.ts +0 -2
- package/vite-base.config.ts +9 -5
- package/vite.config.ts +5 -1
- package/types/index.d.ts +0 -3
package/src/i18n/index.ts
CHANGED
|
@@ -3,7 +3,7 @@ import zhCN from './zh-CN'
|
|
|
3
3
|
|
|
4
4
|
export type I18n = typeof en
|
|
5
5
|
|
|
6
|
-
// @note the
|
|
6
|
+
// @note the key of language is followed by `ISO 639`
|
|
7
7
|
// https://en.wikipedia.org/wiki/ISO_639
|
|
8
8
|
// https://datatracker.ietf.org/doc/html/rfc5646#section-2.1
|
|
9
9
|
export const internal = {
|
|
@@ -14,8 +14,123 @@ export const internal = {
|
|
|
14
14
|
|
|
15
15
|
export const external = {
|
|
16
16
|
'jp-JP': {},
|
|
17
|
-
'zh-TW': {
|
|
17
|
+
'zh-TW': {
|
|
18
|
+
/* Editor */
|
|
19
|
+
placeholder: '鍵入內容...',
|
|
20
|
+
noComment: '「此時無聲勝有聲」',
|
|
21
|
+
send: '發送評論',
|
|
22
|
+
save: '保存評論',
|
|
23
|
+
nick: '昵稱',
|
|
24
|
+
email: '郵箱',
|
|
25
|
+
link: '網址',
|
|
26
|
+
emoticon: '表情',
|
|
27
|
+
preview: '預覽',
|
|
28
|
+
image: '圖片',
|
|
29
|
+
uploadFail: '上傳失敗',
|
|
30
|
+
commentFail: '評論失敗',
|
|
31
|
+
restoredMsg: '內容已自動恢復',
|
|
32
|
+
onlyAdminCanReply: '僅管理員可評論',
|
|
33
|
+
uploadLoginMsg: '填入你的名字郵箱才能上傳哦',
|
|
34
|
+
|
|
35
|
+
/* List */
|
|
36
|
+
counter: '{count} 條評論',
|
|
37
|
+
sortLatest: '最新',
|
|
38
|
+
sortOldest: '最早',
|
|
39
|
+
sortBest: '最熱',
|
|
40
|
+
sortAuthor: '作者',
|
|
41
|
+
openComment: '打開評論',
|
|
42
|
+
closeComment: '關閉評論',
|
|
43
|
+
listLoadFailMsg: '無法獲取評論列表數據',
|
|
44
|
+
listRetry: '點擊重新獲取',
|
|
45
|
+
loadMore: '加載更多',
|
|
46
|
+
|
|
47
|
+
/* Comment */
|
|
48
|
+
admin: '管理員',
|
|
49
|
+
reply: '回覆',
|
|
50
|
+
voteUp: '贊同',
|
|
51
|
+
voteDown: '反對',
|
|
52
|
+
voteFail: '投票失敗',
|
|
53
|
+
readMore: '閱讀更多',
|
|
54
|
+
actionConfirm: '確認操作',
|
|
55
|
+
collapse: '摺疊',
|
|
56
|
+
collapsed: '已摺疊',
|
|
57
|
+
collapsedMsg: '該評論已被系統或管理員摺疊',
|
|
58
|
+
expand: '展開',
|
|
59
|
+
approved: '已審',
|
|
60
|
+
pending: '待審',
|
|
61
|
+
pendingMsg: '審核中,僅本人可見。',
|
|
62
|
+
edit: '編輯',
|
|
63
|
+
editCancel: '取消編輯',
|
|
64
|
+
delete: '刪除',
|
|
65
|
+
deleteConfirm: '確認刪除',
|
|
66
|
+
pin: '置頂',
|
|
67
|
+
unpin: '取消置頂',
|
|
68
|
+
|
|
69
|
+
/* Time */
|
|
70
|
+
seconds: '秒前',
|
|
71
|
+
minutes: '分鐘前',
|
|
72
|
+
hours: '小時前',
|
|
73
|
+
days: '天前',
|
|
74
|
+
now: '剛剛',
|
|
75
|
+
|
|
76
|
+
/* Checker */
|
|
77
|
+
adminCheck: '鍵入密碼來驗證管理員身份:',
|
|
78
|
+
captchaCheck: '鍵入驗證碼繼續:',
|
|
79
|
+
confirm: '確認',
|
|
80
|
+
cancel: '取消',
|
|
81
|
+
|
|
82
|
+
/* Sidebar */
|
|
83
|
+
msgCenter: '通知中心',
|
|
84
|
+
ctrlCenter: '控制中心',
|
|
85
|
+
|
|
86
|
+
/* General */
|
|
87
|
+
frontend: '前端',
|
|
88
|
+
backend: '後端',
|
|
89
|
+
loading: '加載中',
|
|
90
|
+
loadFail: '加載失敗',
|
|
91
|
+
editing: '修改中',
|
|
92
|
+
editFail: '修改失敗',
|
|
93
|
+
deleting: '刪除中',
|
|
94
|
+
deleteFail: '刪除失敗',
|
|
95
|
+
reqGot: '請求響應',
|
|
96
|
+
reqAborted: '請求超時或意外終止',
|
|
97
|
+
},
|
|
18
98
|
'fr-FR': {},
|
|
19
99
|
'de-DE': {},
|
|
20
100
|
'bn-IN': {},
|
|
21
101
|
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* get a locale object by language name
|
|
105
|
+
*/
|
|
106
|
+
function getLocaleSet(lang: string): I18n {
|
|
107
|
+
// normalize a key of language to `ISO 639`
|
|
108
|
+
lang = lang.replace(
|
|
109
|
+
/^([a-zA-Z]+)(-[a-zA-Z]+)?$/,
|
|
110
|
+
(_, p1: string, p2: string) => (p1.toLowerCase() + (p2 || '').toUpperCase())
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
// case not found
|
|
114
|
+
if (!internal[lang]) {
|
|
115
|
+
return internal.en // use `en` by default
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return internal[lang]
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* get a i18n message by key
|
|
123
|
+
*/
|
|
124
|
+
function getI18n(locale: I18n|string, key: keyof I18n, args: {[key: string]: string} = {}) {
|
|
125
|
+
if (typeof locale === 'string') {
|
|
126
|
+
locale = getLocaleSet(locale)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
let str = locale?.[key] || key
|
|
130
|
+
str = str.replace(/\{\s*(\w+?)\s*\}/g, (_, token) => args[token] || '')
|
|
131
|
+
|
|
132
|
+
return str
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export { getLocaleSet, getI18n }
|
|
136
|
+
export default getI18n
|
package/src/i18n/zh-CN.ts
CHANGED
|
@@ -5,6 +5,7 @@ const zhCN: I18n = {
|
|
|
5
5
|
placeholder: '键入内容...',
|
|
6
6
|
noComment: '「此时无声胜有声」',
|
|
7
7
|
send: '发送评论',
|
|
8
|
+
save: '保存评论',
|
|
8
9
|
nick: '昵称',
|
|
9
10
|
email: '邮箱',
|
|
10
11
|
link: '网址',
|
|
@@ -40,10 +41,12 @@ const zhCN: I18n = {
|
|
|
40
41
|
collapse: '折叠',
|
|
41
42
|
collapsed: '已折叠',
|
|
42
43
|
collapsedMsg: '该评论已被系统或管理员折叠',
|
|
43
|
-
expand: '
|
|
44
|
+
expand: '展开',
|
|
44
45
|
approved: '已审',
|
|
45
46
|
pending: '待审',
|
|
46
47
|
pendingMsg: '审核中,仅本人可见。',
|
|
48
|
+
edit: '编辑',
|
|
49
|
+
editCancel: '取消编辑',
|
|
47
50
|
delete: '删除',
|
|
48
51
|
deleteConfirm: '确认删除',
|
|
49
52
|
pin: '置顶',
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import '../style/sidebar-layer.less'
|
|
2
|
-
|
|
3
1
|
import Context from '~/types/context'
|
|
4
2
|
import Component from '@/lib/component'
|
|
5
3
|
import * as Utils from '@/lib/utils'
|
|
@@ -62,7 +60,7 @@ export default class SidebarLayer extends Component {
|
|
|
62
60
|
|
|
63
61
|
// 管理员身份验证 (若身份失效,弹出验证窗口)
|
|
64
62
|
;(async () => {
|
|
65
|
-
const resp = await this.ctx.getApi().loginStatus()
|
|
63
|
+
const resp = await this.ctx.getApi().user.loginStatus()
|
|
66
64
|
if (resp.is_admin && !resp.is_login) {
|
|
67
65
|
this.layer?.hide()
|
|
68
66
|
this.firstShow = true
|
|
@@ -4,7 +4,7 @@ import { Checker } from '.'
|
|
|
4
4
|
|
|
5
5
|
const CaptchaChecker: Checker = {
|
|
6
6
|
request(that, ctx, inputVal) {
|
|
7
|
-
return that.ctx.getApi().captchaCheck(inputVal)
|
|
7
|
+
return that.ctx.getApi().captcha.captchaCheck(inputVal)
|
|
8
8
|
},
|
|
9
9
|
|
|
10
10
|
body(that, ctx) {
|
|
@@ -34,7 +34,7 @@ const CaptchaChecker: Checker = {
|
|
|
34
34
|
if (stop) return
|
|
35
35
|
let isPass = false
|
|
36
36
|
try {
|
|
37
|
-
const resp = await that.ctx.getApi().captchaStatus()
|
|
37
|
+
const resp = await that.ctx.getApi().captcha.captchaStatus()
|
|
38
38
|
isPass = resp.is_pass
|
|
39
39
|
} catch { isPass = false }
|
|
40
40
|
if (isPass) {
|
|
@@ -60,7 +60,7 @@ const CaptchaChecker: Checker = {
|
|
|
60
60
|
// 刷新验证码
|
|
61
61
|
elem.querySelector<HTMLElement>('.atk-captcha-img')!.onclick = () => {
|
|
62
62
|
const imgEl = elem.querySelector('.atk-captcha-img')
|
|
63
|
-
that.ctx.getApi().captchaGet()
|
|
63
|
+
that.ctx.getApi().captcha.captchaGet()
|
|
64
64
|
.then((imgData) => {
|
|
65
65
|
imgEl!.setAttribute('src', imgData)
|
|
66
66
|
})
|
package/src/lib/stat.ts
CHANGED
|
@@ -21,7 +21,7 @@ export async function initCountWidget(p: CountConf) {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
// PV
|
|
24
|
-
const curtPagePvNum = p.pvAdd ? await p.ctx.getApi().pv() : undefined
|
|
24
|
+
const curtPagePvNum = p.pvAdd ? await p.ctx.getApi().page.pv() : undefined
|
|
25
25
|
const pvEl = p.ctx.conf.pvEl
|
|
26
26
|
if (pvEl && document.querySelector(pvEl)) {
|
|
27
27
|
handleStatCount(p, {
|
|
@@ -52,7 +52,7 @@ export async function handleStatCount(
|
|
|
52
52
|
.filter((pageKey) => pageCounts[pageKey] === undefined)
|
|
53
53
|
queryPageKeys = [...new Set(queryPageKeys)] // 去重
|
|
54
54
|
if (queryPageKeys.length > 0) {
|
|
55
|
-
const counts: any = await p.ctx.getApi().stat(args.api, queryPageKeys)
|
|
55
|
+
const counts: any = await p.ctx.getApi().page.stat(args.api, queryPageKeys)
|
|
56
56
|
pageCounts = { ...pageCounts, ...counts }
|
|
57
57
|
}
|
|
58
58
|
|
package/src/lib/ui.ts
CHANGED
|
@@ -32,6 +32,12 @@ export function hideLoading(parentElem: HTMLElement) {
|
|
|
32
32
|
if ($loading) $loading.style.display = 'none'
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
/** 元素加载动画 */
|
|
36
|
+
export function setLoading(val: boolean, parentElem: HTMLElement) {
|
|
37
|
+
if (val) showLoading(parentElem)
|
|
38
|
+
else hideLoading(parentElem)
|
|
39
|
+
}
|
|
40
|
+
|
|
35
41
|
/** 元素是否用户可见 */
|
|
36
42
|
export function isVisible(el: HTMLElement, viewport: HTMLElement = document.documentElement) {
|
|
37
43
|
const viewportHeight = viewport.clientHeight
|
package/src/list/list-lite.ts
CHANGED
|
@@ -7,7 +7,7 @@ import Comment from '../comment'
|
|
|
7
7
|
import Pagination from '../components/pagination'
|
|
8
8
|
import ReadMoreBtn from '../components/read-more-btn'
|
|
9
9
|
import * as ListNest from './list-nest'
|
|
10
|
-
import { backendMinVersion } from '../../package.json'
|
|
10
|
+
import { backendMinVersion, version as ARTALK_VERSION } from '../../package.json'
|
|
11
11
|
|
|
12
12
|
export default class ListLite extends Component {
|
|
13
13
|
protected $commentsWrap: HTMLElement
|
|
@@ -78,40 +78,42 @@ export default class ListLite extends Component {
|
|
|
78
78
|
return this.$commentsWrap
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
+
/** 加载动画 */
|
|
82
|
+
public setLoading(val: boolean, isFirstLoad: boolean = false) {
|
|
83
|
+
this.isLoading = val
|
|
84
|
+
if (isFirstLoad) Ui.setLoading(val, this.$el)
|
|
85
|
+
else if (this.pageMode === 'read-more') this.readMoreBtn!.setLoading(val)
|
|
86
|
+
else if (this.pageMode === 'pagination') this.pagination!.setLoading(val)
|
|
87
|
+
}
|
|
88
|
+
|
|
81
89
|
/** 评论获取 */
|
|
82
90
|
public async fetchComments(offset: number) {
|
|
83
91
|
if (this.isLoading) return
|
|
84
92
|
|
|
93
|
+
const isFirstLoad = (offset === 0)
|
|
94
|
+
const setLoading = (val: boolean) => this.setLoading(val, isFirstLoad)
|
|
95
|
+
|
|
85
96
|
// 加载动画
|
|
86
|
-
|
|
87
|
-
this.isLoading = true
|
|
88
|
-
if (offset === 0) Ui.showLoading(this.$el)
|
|
89
|
-
else if (this.pageMode === 'read-more') this.readMoreBtn!.setLoading(true)
|
|
90
|
-
else if (this.pageMode === 'pagination') this.pagination!.setLoading(true)
|
|
91
|
-
}
|
|
92
|
-
const hideLoading = () => {
|
|
93
|
-
this.isLoading = false
|
|
94
|
-
if (offset === 0) Ui.hideLoading(this.$el)
|
|
95
|
-
else if (this.pageMode === 'read-more') this.readMoreBtn!.setLoading(false)
|
|
96
|
-
else if (this.pageMode === 'pagination') this.pagination!.setLoading(false)
|
|
97
|
-
}
|
|
98
|
-
showLoading()
|
|
97
|
+
setLoading(true)
|
|
99
98
|
|
|
100
99
|
// 事件通知(开始加载评论)
|
|
101
100
|
this.ctx.trigger('list-load')
|
|
102
101
|
|
|
103
102
|
// 清空评论(加载按钮)
|
|
104
|
-
if (this.pageMode === 'read-more'
|
|
103
|
+
if (isFirstLoad && this.pageMode === 'read-more') {
|
|
104
|
+
this.ctx.clearAllComments()
|
|
105
|
+
}
|
|
105
106
|
|
|
106
107
|
// 请求评论数据
|
|
107
108
|
let listData: ListData
|
|
108
109
|
try {
|
|
109
|
-
listData = await this.ctx.getApi().
|
|
110
|
+
listData = await this.ctx.getApi().comment
|
|
111
|
+
.get(offset, this.pageSize, this.flatMode, this.paramsEditor)
|
|
110
112
|
} catch (e: any) {
|
|
111
113
|
this.onError(e.msg || String(e), offset, e.data)
|
|
112
114
|
throw e
|
|
113
115
|
} finally {
|
|
114
|
-
|
|
116
|
+
setLoading(false)
|
|
115
117
|
}
|
|
116
118
|
|
|
117
119
|
// 清除原有错误
|
|
@@ -124,7 +126,7 @@ export default class ListLite extends Component {
|
|
|
124
126
|
this.onError(String(e), offset)
|
|
125
127
|
throw e
|
|
126
128
|
} finally {
|
|
127
|
-
|
|
129
|
+
setLoading(false)
|
|
128
130
|
}
|
|
129
131
|
}
|
|
130
132
|
|
|
@@ -194,15 +196,13 @@ export default class ListLite extends Component {
|
|
|
194
196
|
const at = this.scrollListenerAt || document
|
|
195
197
|
if (this.autoLoadScrollEvent) at.removeEventListener('scroll', this.autoLoadScrollEvent) // 解除原有
|
|
196
198
|
this.autoLoadScrollEvent = () => {
|
|
197
|
-
if (this.pageMode !== 'read-more'
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
if (this.isLoading) return
|
|
199
|
+
if (this.pageMode !== 'read-more'
|
|
200
|
+
|| !this.readMoreBtn?.hasMore
|
|
201
|
+
|| this.isLoading
|
|
202
|
+
) return
|
|
202
203
|
|
|
203
204
|
const $target = this.$el.querySelector<HTMLElement>('.atk-list-comments-wrap > .atk-comment-wrap:nth-last-child(3)') // 获取倒数第3个评论元素
|
|
204
|
-
if (
|
|
205
|
-
if (Ui.isVisible($target, this.scrollListenerAt)) {
|
|
205
|
+
if ($target && Ui.isVisible($target, this.scrollListenerAt)) {
|
|
206
206
|
this.readMoreBtn.click() // 自动点击加载更多按钮
|
|
207
207
|
}
|
|
208
208
|
}
|
|
@@ -429,6 +429,14 @@ export default class ListLite extends Component {
|
|
|
429
429
|
this.ctx.trigger('list-inserted', commentData)
|
|
430
430
|
}
|
|
431
431
|
|
|
432
|
+
/** 更新评论 */
|
|
433
|
+
public updateComment(commentData: CommentData) {
|
|
434
|
+
const comment = this.ctx.findComment(commentData.id)
|
|
435
|
+
if (comment) {
|
|
436
|
+
comment.setData(commentData)
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
432
440
|
/** 更新未读数据 */
|
|
433
441
|
public updateUnread(notifies: NotifyData[]) {
|
|
434
442
|
this.unread = notifies
|
package/src/list/list.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import '../style/list.less'
|
|
2
|
-
|
|
3
1
|
import { ListData, NotifyData } from '~/types/artalk-data'
|
|
4
2
|
import Context from '~/types/context'
|
|
3
|
+
import { version as ARTALK_VERSION } from '../../package.json'
|
|
5
4
|
import * as Utils from '../lib/utils'
|
|
6
5
|
import * as Ui from '../lib/ui'
|
|
7
6
|
import ListHTML from './list.html?raw'
|
|
@@ -143,7 +142,7 @@ export default class List extends ListLite {
|
|
|
143
142
|
// 已阅 API
|
|
144
143
|
const notifyKey = Utils.getQueryParam('atk_notify_key')
|
|
145
144
|
if (notifyKey) {
|
|
146
|
-
this.ctx.getApi().markRead(notifyKey)
|
|
145
|
+
this.ctx.getApi().user.markRead(notifyKey)
|
|
147
146
|
.then(() => {
|
|
148
147
|
this.unread = this.unread.filter(o => o.comment_id !== commentId)
|
|
149
148
|
this.updateUnread(this.unread)
|
|
@@ -175,7 +174,7 @@ export default class List extends ListLite {
|
|
|
175
174
|
if (!this.data || !this.data.page) return
|
|
176
175
|
|
|
177
176
|
this.ctx.editorShowLoading()
|
|
178
|
-
this.ctx.getApi().pageEdit(this.data.page)
|
|
177
|
+
this.ctx.getApi().page.pageEdit(this.data.page)
|
|
179
178
|
.then((page) => {
|
|
180
179
|
if (this.data)
|
|
181
180
|
this.data.page = { ...page }
|
package/src/main.ts
CHANGED
package/src/style/comment.less
CHANGED
package/src/style/main.less
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
@import "./_color.less";
|
|
2
|
+
@import './comment.less';
|
|
3
|
+
@import './list.less';
|
|
4
|
+
@import './pagination.less';
|
|
5
|
+
@import './editor.less';
|
|
6
|
+
@import './sidebar-layer.less';
|
|
2
7
|
|
|
3
8
|
.artalk {
|
|
4
9
|
position: relative;
|
|
@@ -460,4 +465,5 @@ i.atk-icon {
|
|
|
460
465
|
|
|
461
466
|
img[atk-emoticon] {
|
|
462
467
|
max-height: 60px;
|
|
468
|
+
display: initial;
|
|
463
469
|
}
|
package/test/CSRF.html
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
7
|
+
<title>CSRF Test</title>
|
|
8
|
+
|
|
9
|
+
<!-- 解除注释来测试当 referer 为空时的行为 -->
|
|
10
|
+
<!-- <meta name="referrer" content="never"> -->
|
|
11
|
+
</head>
|
|
12
|
+
<body>
|
|
13
|
+
|
|
14
|
+
<form action="http://localhost:23366/api/add" method="post">
|
|
15
|
+
<input type="text" name="name" value="admin">
|
|
16
|
+
<input type="text" name="email" value="test@example.com">
|
|
17
|
+
<input type="text" name="link" value="">
|
|
18
|
+
<input type="text" name="content" value="CSRF Success">
|
|
19
|
+
<input type="text" name="rid" value="0">
|
|
20
|
+
<input type="text" name="page_key" value="https://artalk.js.org/">
|
|
21
|
+
<input type="text" name="site_name" value="ArtalkDocs">
|
|
22
|
+
<button type="submit">Fire!</button>
|
|
23
|
+
</form>
|
|
24
|
+
|
|
25
|
+
</body>
|
|
26
|
+
</html>
|
package/types/artalk-data.d.ts
CHANGED
package/types/context.d.ts
CHANGED
|
@@ -38,7 +38,7 @@ export default interface ContextApi {
|
|
|
38
38
|
/* 设置持有的同事类 (中介者模式) */
|
|
39
39
|
setApi(api: Api): void
|
|
40
40
|
setEditor(editor: Editor): void
|
|
41
|
-
setList(list
|
|
41
|
+
setList(list?: ListLite): void
|
|
42
42
|
setSidebarLayer(list: SidebarLayer): void
|
|
43
43
|
setCheckerLauncher(checkerLauncher: CheckerLauncher): void
|
|
44
44
|
|
|
@@ -63,21 +63,36 @@ export default interface ContextApi {
|
|
|
63
63
|
/** 插入评论 */
|
|
64
64
|
insertComment(commentData: CommentData): void
|
|
65
65
|
|
|
66
|
+
/** 更新评论 */
|
|
67
|
+
updateComment(commentData: CommentData): void
|
|
68
|
+
|
|
66
69
|
/** 评论回复 */
|
|
67
70
|
replyComment(commentData: CommentData, $comment: HTMLElement, scroll?: boolean): void
|
|
68
71
|
|
|
69
72
|
/** 取消回复评论 */
|
|
70
73
|
cancelReplyComment(): void
|
|
71
74
|
|
|
75
|
+
/** 编辑评论 */
|
|
76
|
+
editComment(commentData: CommentData, $comment: HTMLElement): void
|
|
77
|
+
|
|
78
|
+
/** 取消编辑评论 */
|
|
79
|
+
cancelEditComment(): void
|
|
80
|
+
|
|
72
81
|
/** 更新通知数据 */
|
|
73
82
|
updateNotifies(notifies: NotifyData[]): void
|
|
74
83
|
|
|
75
84
|
/** 列表 - 重新加载数据 */
|
|
76
85
|
listReload(): void
|
|
77
86
|
|
|
87
|
+
/** 列表 - 重新加载数据 (别名) */
|
|
88
|
+
reload(): void
|
|
89
|
+
|
|
78
90
|
/** 列表 - UI 更新 */
|
|
79
91
|
listRefreshUI(): void
|
|
80
92
|
|
|
93
|
+
/** 列表 - HashGoto 功能检测 */
|
|
94
|
+
listHashGotoCheck(): void
|
|
95
|
+
|
|
81
96
|
/** 显示侧边栏 */
|
|
82
97
|
showSidebar(payload?: SidebarShowPayload): void
|
|
83
98
|
|
|
@@ -125,4 +140,7 @@ export default interface ContextApi {
|
|
|
125
140
|
|
|
126
141
|
/** i18n 翻译 */
|
|
127
142
|
$t(key: keyof I18n, args?: {[key: string]: string}): string
|
|
143
|
+
|
|
144
|
+
/** 设置暗黑模式 */
|
|
145
|
+
setDarkMode(darkMode: boolean): void
|
|
128
146
|
}
|
package/types/global.d.ts
CHANGED
package/vite-base.config.ts
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import { defineConfig } from 'vite'
|
|
2
2
|
import tsconfigPaths from 'vite-tsconfig-paths'
|
|
3
|
-
import {
|
|
3
|
+
import { resolve } from 'path'
|
|
4
|
+
import dts from 'vite-plugin-dts'
|
|
4
5
|
|
|
5
6
|
export default defineConfig({
|
|
6
|
-
define: {
|
|
7
|
-
ARTALK_VERSION: JSON.stringify(version),
|
|
8
|
-
},
|
|
9
7
|
css: {
|
|
10
8
|
preprocessorOptions: {
|
|
11
9
|
less: {
|
|
@@ -13,5 +11,11 @@ export default defineConfig({
|
|
|
13
11
|
},
|
|
14
12
|
},
|
|
15
13
|
},
|
|
16
|
-
|
|
14
|
+
resolve: {
|
|
15
|
+
alias: {
|
|
16
|
+
'@': resolve(__dirname, 'src'),
|
|
17
|
+
'~': resolve(__dirname),
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
plugins: [tsconfigPaths(), dts()],
|
|
17
21
|
})
|
package/vite.config.ts
CHANGED
|
@@ -17,7 +17,11 @@ export default Utils.mergeDeep(baseConf, defineConfig({
|
|
|
17
17
|
},
|
|
18
18
|
rollupOptions: {
|
|
19
19
|
output: {
|
|
20
|
-
assetFileNames: (assetInfo) => (/\.css$/.test(assetInfo.name) ? "Artalk.css" : "[name].[ext]")
|
|
20
|
+
assetFileNames: (assetInfo) => (/\.css$/.test(assetInfo.name || '') ? "Artalk.css" : "[name].[ext]"),
|
|
21
|
+
|
|
22
|
+
// Entry module "src/main.ts" is using named and default exports together.
|
|
23
|
+
// Consumers of your bundle will have to use `Artalk["default"]` to access the default export
|
|
24
|
+
exports: 'named'
|
|
21
25
|
}
|
|
22
26
|
}
|
|
23
27
|
},
|
package/types/index.d.ts
DELETED