artalk 2.6.4 → 2.7.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 +7794 -7483
- package/dist/Artalk.iife.js +1 -1
- package/dist/Artalk.js +1 -1
- package/dist/ArtalkLite.css +1 -1
- package/dist/ArtalkLite.es.js +5587 -5276
- package/dist/ArtalkLite.iife.js +1 -1
- package/dist/ArtalkLite.js +1 -1
- package/dist/src/api/{api-base.d.ts → _base.d.ts} +3 -7
- package/dist/src/api/_options.d.ts +19 -0
- package/dist/src/api/{request.d.ts → _request.d.ts} +3 -3
- package/dist/src/api/admin.d.ts +1 -1
- package/dist/src/api/captcha.d.ts +1 -1
- package/dist/src/api/comment.d.ts +2 -2
- package/dist/src/api/index.d.ts +2 -4
- package/dist/src/api/page.d.ts +3 -3
- package/dist/src/api/site.d.ts +2 -2
- package/dist/src/api/system.d.ts +13 -3
- package/dist/src/api/upload.d.ts +1 -1
- package/dist/src/api/user.d.ts +2 -2
- package/dist/src/artalk.d.ts +14 -37
- package/dist/src/comment/actions.d.ts +1 -1
- package/dist/src/comment/comment.d.ts +3 -4
- package/dist/src/comment/render.d.ts +21 -3
- package/dist/src/comment/renders/actions.d.ts +2 -2
- package/dist/src/comment/renders/avatar.d.ts +2 -2
- package/dist/src/comment/renders/content.d.ts +2 -2
- package/dist/src/comment/renders/header.d.ts +2 -2
- package/dist/src/comment/renders/index.d.ts +2 -2
- package/dist/src/comment/renders/pending.d.ts +2 -2
- package/dist/src/comment/renders/reply-at.d.ts +2 -2
- package/dist/src/comment/renders/reply-to.d.ts +2 -2
- package/dist/src/components/action-btn.d.ts +3 -5
- package/dist/src/{lib → components}/checker/admin.d.ts +1 -1
- package/dist/src/{lib → components}/checker/captcha-renders.d.ts +1 -1
- package/dist/src/{lib → components}/checker/captcha.d.ts +1 -1
- package/dist/src/{lib → components}/checker/index.d.ts +10 -7
- package/dist/src/components/dark-mode.d.ts +2 -0
- package/dist/src/components/dialog.d.ts +1 -3
- package/dist/src/components/error-dialog.d.ts +8 -0
- package/dist/src/components/pagination.d.ts +4 -3
- package/dist/src/components/read-more-btn.d.ts +3 -3
- package/dist/src/config.d.ts +24 -8
- package/dist/src/context.d.ts +16 -35
- package/dist/src/data.d.ts +27 -0
- package/dist/src/defaults.d.ts +1 -1
- package/dist/src/editor/editor.d.ts +14 -61
- package/dist/src/editor/state.d.ts +20 -0
- package/dist/src/i18n/zh-CN.d.ts +1 -1
- package/dist/src/layer/index.d.ts +4 -3
- package/dist/src/layer/layer-manager.d.ts +9 -0
- package/dist/src/layer/layer.d.ts +8 -28
- package/dist/src/layer/scrollbar-helper.d.ts +5 -0
- package/dist/src/layer/sidebar-layer.d.ts +6 -6
- package/dist/src/layer/wrap.d.ts +13 -0
- package/dist/src/lib/component.d.ts +3 -8
- package/dist/src/lib/event-manager.d.ts +28 -0
- package/dist/src/lib/highlight.d.ts +1 -0
- package/dist/src/lib/marked-renderer.d.ts +4 -0
- package/dist/src/lib/marked.d.ts +4 -2
- package/dist/src/lib/sanitizer.d.ts +1 -0
- package/dist/src/lib/ui.d.ts +3 -2
- package/dist/src/lib/user.d.ts +8 -5
- package/dist/src/lib/utils.d.ts +11 -5
- package/dist/src/list/comment.d.ts +3 -0
- package/dist/src/list/layout.d.ts +23 -0
- package/dist/src/list/list.d.ts +13 -25
- package/dist/src/list/{list-nest.d.ts → nest.d.ts} +1 -1
- package/dist/src/list/page.d.ts +2 -0
- package/dist/src/list/paginator/index.d.ts +22 -7
- package/dist/src/list/paginator/read-more.d.ts +18 -0
- package/dist/src/list/paginator/up-down.d.ts +14 -0
- package/dist/src/main.d.ts +2 -0
- package/dist/src/plugins/admin-only-elem.d.ts +2 -0
- package/dist/src/plugins/conf-remoter.d.ts +2 -0
- package/dist/src/plugins/dark-mode.d.ts +2 -0
- package/dist/src/plugins/editor/_kit.d.ts +27 -0
- package/dist/src/plugins/editor/_plug.d.ts +29 -0
- package/dist/src/plugins/editor/_sample.d.ts +5 -0
- package/dist/src/plugins/editor/closable.d.ts +7 -0
- package/dist/src/{editor/plugs/emoticons-plug.d.ts → plugins/editor/emoticons.d.ts} +10 -16
- package/dist/src/plugins/editor/header-event.d.ts +6 -0
- package/dist/src/plugins/editor/header-link.d.ts +6 -0
- package/dist/src/plugins/editor/header-user.d.ts +16 -0
- package/dist/src/plugins/editor/index.d.ts +6 -0
- package/dist/src/plugins/editor/local-storage.d.ts +6 -0
- package/dist/src/plugins/editor/mover.d.ts +6 -0
- package/dist/src/plugins/editor/preview.d.ts +7 -0
- package/dist/src/plugins/editor/state-edit.d.ts +11 -0
- package/dist/src/plugins/editor/state-reply.d.ts +8 -0
- package/dist/src/plugins/editor/submit-add.d.ts +18 -0
- package/dist/src/plugins/editor/submit-btn.d.ts +5 -0
- package/dist/src/plugins/editor/submit.d.ts +17 -0
- package/dist/src/plugins/editor/textarea.d.ts +8 -0
- package/dist/src/plugins/editor/upload.d.ts +9 -0
- package/dist/src/plugins/editor-kit.d.ts +42 -0
- package/dist/src/plugins/index.d.ts +2 -0
- package/dist/src/plugins/list/copyright.d.ts +2 -0
- package/dist/src/plugins/list/count.d.ts +2 -0
- package/dist/src/plugins/list/dropdown.d.ts +2 -0
- package/dist/src/plugins/list/error-dialog.d.ts +2 -0
- package/dist/src/plugins/list/fetch.d.ts +2 -0
- package/dist/src/plugins/list/goto-first.d.ts +3 -0
- package/dist/src/plugins/list/goto.d.ts +2 -0
- package/dist/src/plugins/list/index.d.ts +3 -0
- package/dist/src/plugins/list/loading.d.ts +2 -0
- package/dist/src/plugins/list/no-comment.d.ts +2 -0
- package/dist/src/plugins/list/reach-bottom.d.ts +2 -0
- package/dist/src/plugins/list/sidebar-btn.d.ts +2 -0
- package/dist/src/plugins/list/time-ticking.d.ts +3 -0
- package/dist/src/plugins/list/unread-badge.d.ts +2 -0
- package/dist/src/plugins/list/unread.d.ts +2 -0
- package/dist/src/plugins/list/with-editor.d.ts +2 -0
- package/dist/src/plugins/markdown.d.ts +2 -0
- package/dist/src/plugins/stat.d.ts +13 -0
- package/dist/src/plugins/version-check.d.ts +2 -0
- package/dist/src/service.d.ts +19 -23
- package/dist/tests/e2e/comment-add.spec.d.ts +1 -0
- package/dist/types/config.d.ts +110 -0
- package/dist/types/context.d.ts +56 -136
- package/dist/types/data.d.ts +194 -0
- package/dist/types/editor.d.ts +76 -0
- package/dist/types/event.d.ts +36 -25
- package/dist/types/index.d.ts +7 -0
- package/dist/types/plugin.d.ts +2 -0
- package/dist/types/sidebar.d.ts +3 -0
- package/package.json +5 -2
- package/src/api/_base.ts +18 -0
- package/src/api/_options.ts +25 -0
- package/src/api/{request.ts → _request.ts} +19 -18
- package/src/api/admin.ts +1 -1
- package/src/api/captcha.ts +1 -1
- package/src/api/comment.ts +4 -4
- package/src/api/index.ts +3 -12
- package/src/api/page.ts +5 -5
- package/src/api/site.ts +2 -2
- package/src/api/system.ts +19 -7
- package/src/api/upload.ts +3 -3
- package/src/api/user.ts +3 -3
- package/src/artalk.ts +49 -97
- package/src/comment/actions.ts +1 -4
- package/src/comment/comment.ts +9 -6
- package/src/comment/render.ts +27 -5
- package/src/comment/renders/actions.ts +45 -45
- package/src/comment/renders/avatar.ts +7 -7
- package/src/comment/renders/content.ts +18 -18
- package/src/comment/renders/header.ts +13 -13
- package/src/comment/renders/index.ts +3 -3
- package/src/comment/renders/pending.ts +5 -5
- package/src/comment/renders/reply-at.ts +8 -8
- package/src/comment/renders/reply-to.ts +13 -13
- package/src/components/action-btn.ts +11 -13
- package/src/{lib → components}/checker/admin.ts +6 -6
- package/src/{lib → components}/checker/captcha-renders.ts +7 -6
- package/src/{lib → components}/checker/captcha.ts +1 -1
- package/src/{lib → components}/checker/index.ts +21 -26
- package/src/components/dark-mode.ts +30 -0
- package/src/components/dialog.ts +4 -9
- package/src/components/error-dialog.ts +35 -0
- package/src/components/pagination.ts +10 -6
- package/src/components/read-more-btn.ts +7 -7
- package/src/config.ts +60 -6
- package/src/context.ts +39 -144
- package/src/data.ts +110 -0
- package/src/defaults.ts +1 -1
- package/src/editor/editor.ts +39 -152
- package/src/editor/state.ts +52 -0
- package/src/i18n/zh-CN.ts +1 -1
- package/src/layer/index.ts +4 -4
- package/src/layer/layer-manager.ts +32 -0
- package/src/layer/layer.ts +20 -144
- package/src/layer/scrollbar-helper.ts +24 -0
- package/src/layer/sidebar-layer.ts +74 -64
- package/src/layer/wrap.ts +77 -0
- package/src/lib/component.ts +4 -11
- package/src/lib/detect.ts +1 -1
- package/src/lib/event-manager.ts +47 -0
- package/src/lib/highlight.ts +5 -0
- package/src/lib/marked-renderer.ts +32 -0
- package/src/lib/marked.ts +43 -104
- package/src/lib/sanitizer.ts +57 -0
- package/src/lib/ui.ts +22 -15
- package/src/lib/user.ts +21 -16
- package/src/lib/utils.ts +29 -18
- package/src/list/comment.ts +20 -0
- package/src/list/layout.ts +120 -0
- package/src/list/list.html +3 -1
- package/src/list/list.ts +56 -200
- package/src/list/{list-nest.ts → nest.ts} +1 -1
- package/src/list/page.ts +97 -0
- package/src/list/paginator/index.ts +20 -8
- package/src/list/paginator/read-more.ts +67 -0
- package/src/list/paginator/up-down.ts +51 -0
- package/src/main.ts +2 -0
- package/src/plugins/admin-only-elem.ts +40 -0
- package/src/plugins/conf-remoter.ts +54 -0
- package/src/plugins/dark-mode.ts +12 -0
- package/src/plugins/editor/_kit.ts +57 -0
- package/src/plugins/editor/_plug.ts +60 -0
- package/src/plugins/editor/_sample.ts +8 -0
- package/src/plugins/editor/closable.ts +44 -0
- package/src/{editor/plugs/emoticons-plug.ts → plugins/editor/emoticons.ts} +51 -52
- package/src/plugins/editor/header-event.ts +35 -0
- package/src/plugins/editor/header-link.ts +31 -0
- package/src/plugins/editor/header-user.ts +98 -0
- package/src/plugins/editor/index.ts +40 -0
- package/src/plugins/editor/local-storage.ts +36 -0
- package/src/plugins/editor/mover.ts +29 -0
- package/src/plugins/editor/preview.ts +41 -0
- package/src/plugins/editor/state-edit.ts +116 -0
- package/src/plugins/editor/state-reply.ts +91 -0
- package/src/plugins/editor/submit-add.ts +33 -0
- package/src/plugins/editor/submit-btn.ts +25 -0
- package/src/plugins/editor/submit.ts +73 -0
- package/src/plugins/editor/textarea.ts +55 -0
- package/src/plugins/editor/upload.ts +145 -0
- package/src/plugins/editor-kit.ts +161 -0
- package/src/plugins/index.ts +16 -0
- package/src/plugins/list/copyright.ts +16 -0
- package/src/plugins/list/count.ts +31 -0
- package/src/plugins/list/dropdown.ts +95 -0
- package/src/plugins/list/error-dialog.ts +22 -0
- package/src/plugins/list/fetch.ts +64 -0
- package/src/plugins/list/goto-first.ts +23 -0
- package/src/plugins/list/goto.ts +72 -0
- package/src/plugins/list/index.ts +25 -0
- package/src/plugins/list/loading.ts +17 -0
- package/src/plugins/list/no-comment.ts +25 -0
- package/src/plugins/list/reach-bottom.ts +46 -0
- package/src/plugins/list/sidebar-btn.ts +39 -0
- package/src/plugins/list/time-ticking.ts +22 -0
- package/src/plugins/list/unread-badge.ts +26 -0
- package/src/plugins/list/unread.ts +38 -0
- package/src/plugins/list/with-editor.ts +59 -0
- package/src/plugins/markdown.ts +10 -0
- package/src/plugins/stat.ts +79 -0
- package/src/plugins/version-check.ts +37 -0
- package/src/service.ts +34 -61
- package/src/style/comment.scss +5 -7
- package/src/style/editor.scss +5 -0
- package/src/style/list.scss +1 -1
- package/types/{artalk-config.d.ts → config.ts} +16 -2
- package/types/context.ts +84 -0
- package/types/{artalk-data.d.ts → data.ts} +41 -0
- package/types/editor.ts +95 -0
- package/types/event.ts +40 -0
- package/types/index.ts +7 -0
- package/types/plugin.ts +3 -0
- package/types/sidebar.ts +3 -0
- package/dist/src/comment/render-ctx.d.ts +0 -23
- package/dist/src/editor/edit-mode.d.ts +0 -9
- package/dist/src/editor/funcs.d.ts +0 -2
- package/dist/src/editor/index.d.ts +0 -2
- package/dist/src/editor/mover.d.ts +0 -8
- package/dist/src/editor/plug-manager.d.ts +0 -17
- package/dist/src/editor/plugs/editor-plug.d.ts +0 -38
- package/dist/src/editor/plugs/header-input-plug.d.ts +0 -13
- package/dist/src/editor/plugs/preview-plug.d.ts +0 -10
- package/dist/src/editor/plugs/upload-plug.d.ts +0 -10
- package/dist/src/editor/reply.d.ts +0 -9
- package/dist/src/editor/submit.d.ts +0 -16
- package/dist/src/lib/dark-mode.d.ts +0 -4
- package/dist/src/lib/stat.d.ts +0 -15
- package/dist/src/list/index.d.ts +0 -2
- package/dist/src/list/list-lite.d.ts +0 -77
- package/dist/src/list/list-ui.d.ts +0 -16
- package/dist/src/list/paginator/adaptors/index.d.ts +0 -12
- package/dist/src/list/paginator/adaptors/pagination.d.ts +0 -9
- package/dist/src/list/paginator/adaptors/read-more.d.ts +0 -10
- package/dist/src/list/paginator/holder.d.ts +0 -16
- package/dist/types/artalk-config.d.ts +0 -139
- package/dist/types/artalk-data.d.ts +0 -230
- package/dist/types/plug.d.ts +0 -4
- package/src/api/api-base.ts +0 -27
- package/src/comment/render-ctx.ts +0 -30
- package/src/editor/edit-mode.ts +0 -105
- package/src/editor/funcs.ts +0 -150
- package/src/editor/index.ts +0 -3
- package/src/editor/mover.ts +0 -43
- package/src/editor/plug-manager.ts +0 -172
- package/src/editor/plugs/editor-plug.ts +0 -69
- package/src/editor/plugs/header-input-plug.ts +0 -79
- package/src/editor/plugs/preview-plug.ts +0 -44
- package/src/editor/plugs/upload-plug.ts +0 -122
- package/src/editor/reply.ts +0 -62
- package/src/editor/submit.ts +0 -99
- package/src/lib/dark-mode.ts +0 -48
- package/src/lib/stat.ts +0 -71
- package/src/list/index.ts +0 -3
- package/src/list/list-lite.ts +0 -424
- package/src/list/list-ui.ts +0 -137
- package/src/list/paginator/adaptors/index.ts +0 -21
- package/src/list/paginator/adaptors/pagination.ts +0 -42
- package/src/list/paginator/adaptors/read-more.ts +0 -56
- package/src/list/paginator/holder.ts +0 -56
- package/types/context.d.ts +0 -142
- package/types/event.d.ts +0 -28
- package/types/plug.d.ts +0 -4
- /package/src/{editor/plugs/emoticons-plug.scss → plugins/editor/emoticons.scss} +0 -0
- /package/src/{editor/plugs/preview-plug.scss → plugins/editor/preview.scss} +0 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { ListFetchParams, ArtalkPlugin } from '~/types'
|
|
2
|
+
|
|
3
|
+
export const Fetch: ArtalkPlugin = (ctx) => {
|
|
4
|
+
ctx.on('list-fetch', (_params) => {
|
|
5
|
+
if (ctx.getData().getLoading()) return
|
|
6
|
+
ctx.getData().setLoading(true)
|
|
7
|
+
|
|
8
|
+
const params: ListFetchParams = {
|
|
9
|
+
// default params
|
|
10
|
+
offset: 0,
|
|
11
|
+
limit: ctx.conf.pagination.pageSize,
|
|
12
|
+
flatMode: ctx.conf.flatMode as boolean, // always be boolean because had been handled in Artalk.init
|
|
13
|
+
paramsModifier: ctx.conf.listFetchParamsModifier,
|
|
14
|
+
..._params
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// must before other function call
|
|
18
|
+
ctx.getData().setListLastFetch({
|
|
19
|
+
params
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
ctx.getApi().comment
|
|
23
|
+
.get(params.offset, params.limit, params.flatMode, params.paramsModifier)
|
|
24
|
+
.then((data) => {
|
|
25
|
+
// Must before all other function call and event trigger,
|
|
26
|
+
// because it will depend on the lastData
|
|
27
|
+
// TODO this is global variable, easy to use, but not good, consider to refactor.
|
|
28
|
+
// refactor work is hard, because it is used in many places.
|
|
29
|
+
ctx.getData().setListLastFetch({ params, data })
|
|
30
|
+
|
|
31
|
+
// 装置评论
|
|
32
|
+
ctx.getData().loadComments(data.comments)
|
|
33
|
+
|
|
34
|
+
// 更新页面数据
|
|
35
|
+
ctx.getData().updatePage(data.page)
|
|
36
|
+
|
|
37
|
+
// 未读消息提示功能
|
|
38
|
+
ctx.getData().updateUnreads(data.unread || [])
|
|
39
|
+
|
|
40
|
+
// trigger events when success
|
|
41
|
+
params.onSuccess && params.onSuccess(data)
|
|
42
|
+
|
|
43
|
+
ctx.trigger('list-fetched', { params, data })
|
|
44
|
+
})
|
|
45
|
+
.catch((e) => {
|
|
46
|
+
// 显示错误对话框
|
|
47
|
+
const error = {
|
|
48
|
+
msg: e.msg || String(e),
|
|
49
|
+
data: e.data
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
params.onError && params.onError(error)
|
|
53
|
+
|
|
54
|
+
// trigger events when error
|
|
55
|
+
ctx.trigger('list-error', error)
|
|
56
|
+
ctx.trigger('list-fetched', { params, error })
|
|
57
|
+
|
|
58
|
+
throw e
|
|
59
|
+
})
|
|
60
|
+
.finally(() => {
|
|
61
|
+
ctx.getData().setLoading(false)
|
|
62
|
+
})
|
|
63
|
+
})
|
|
64
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { ArtalkPlugin } from '~/types'
|
|
2
|
+
import * as Utils from '@/lib/utils'
|
|
3
|
+
|
|
4
|
+
/** List scroll to the first comment */
|
|
5
|
+
export const GotoFirst: ArtalkPlugin = (ctx) => {
|
|
6
|
+
const handler = () => {
|
|
7
|
+
const list = ctx.get('list')
|
|
8
|
+
|
|
9
|
+
const $relative = ctx.conf.scrollRelativeTo && ctx.conf.scrollRelativeTo()
|
|
10
|
+
;($relative || window).scroll({
|
|
11
|
+
top: Utils.getOffset(list.$el, $relative).top,
|
|
12
|
+
left: 0,
|
|
13
|
+
})
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
ctx.on('inited', () => {
|
|
17
|
+
ctx.on('list-goto-first', handler)
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
ctx.on('destroy', () => {
|
|
21
|
+
ctx.off('list-goto-first', handler)
|
|
22
|
+
})
|
|
23
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type { ArtalkPlugin } from '~/types'
|
|
2
|
+
import * as Utils from '@/lib/utils'
|
|
3
|
+
import * as Ui from '@/lib/ui'
|
|
4
|
+
|
|
5
|
+
export const Goto: ArtalkPlugin = (ctx) => {
|
|
6
|
+
let delayGoto = true
|
|
7
|
+
|
|
8
|
+
const check = () => {
|
|
9
|
+
const commentID = extractCommentID()
|
|
10
|
+
if (!commentID) return
|
|
11
|
+
|
|
12
|
+
// trigger event
|
|
13
|
+
ctx.trigger('list-goto', commentID)
|
|
14
|
+
|
|
15
|
+
// reset delayGoto
|
|
16
|
+
delayGoto = true
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const hashChangeHandler = () => {
|
|
20
|
+
delayGoto = false
|
|
21
|
+
check()
|
|
22
|
+
}
|
|
23
|
+
ctx.on('inited', () => {
|
|
24
|
+
window.addEventListener('hashchange', hashChangeHandler)
|
|
25
|
+
ctx.on('list-loaded', check)
|
|
26
|
+
})
|
|
27
|
+
ctx.on('destroy', () => {
|
|
28
|
+
window.removeEventListener('hashchange', hashChangeHandler)
|
|
29
|
+
ctx.off('list-loaded', check)
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
let foundID = 0
|
|
33
|
+
ctx.on('list-goto', (commentID) => {
|
|
34
|
+
if (foundID === commentID) return
|
|
35
|
+
|
|
36
|
+
const comment = ctx.get('list').getCommentNodes().find(c => c.getID() === commentID)
|
|
37
|
+
if (!comment) return
|
|
38
|
+
|
|
39
|
+
foundID = commentID
|
|
40
|
+
|
|
41
|
+
// 若父评论存在 “子评论部分” 限高,取消限高
|
|
42
|
+
comment.getParents().forEach((p) => {
|
|
43
|
+
p.getRender().heightLimitRemoveForChildren()
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
const goTo = () => {
|
|
47
|
+
Ui.scrollIntoView(comment.getEl(), false)
|
|
48
|
+
|
|
49
|
+
comment.getEl().classList.remove('atk-flash-once')
|
|
50
|
+
window.setTimeout(() => {
|
|
51
|
+
comment.getEl().classList.add('atk-flash-once')
|
|
52
|
+
}, 150)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (!delayGoto) goTo()
|
|
56
|
+
else window.setTimeout(() => goTo(), 350)
|
|
57
|
+
})
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function extractCommentID(): number|null {
|
|
61
|
+
// try get from query
|
|
62
|
+
let commentId = Number(Utils.getQueryParam('atk_comment')) // same as backend GetReplyLink()
|
|
63
|
+
|
|
64
|
+
// fail over to get from hash
|
|
65
|
+
if (!commentId) {
|
|
66
|
+
const match = window.location.hash.match(/#atk-comment-([0-9]+)/)
|
|
67
|
+
if (!match || !match[1] || Number.isNaN(Number(match[1]))) return null
|
|
68
|
+
commentId = Number(match[1])
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return commentId || null
|
|
72
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { ArtalkPlugin } from '~/types'
|
|
2
|
+
import { WithEditor } from './with-editor'
|
|
3
|
+
import { Unread } from './unread'
|
|
4
|
+
import { Count } from './count'
|
|
5
|
+
import { SidebarBtn } from './sidebar-btn'
|
|
6
|
+
import { UnreadBadge } from './unread-badge'
|
|
7
|
+
import { Goto } from './goto'
|
|
8
|
+
import { Copyright } from './copyright'
|
|
9
|
+
import { NoComment } from './no-comment'
|
|
10
|
+
import { Dropdown } from './dropdown'
|
|
11
|
+
import { TimeTicking } from './time-ticking'
|
|
12
|
+
import { ErrorDialog } from './error-dialog'
|
|
13
|
+
import { Loading } from './loading'
|
|
14
|
+
import { Fetch } from './fetch'
|
|
15
|
+
import { ReachBottom } from './reach-bottom'
|
|
16
|
+
import { GotoFirst } from './goto-first'
|
|
17
|
+
|
|
18
|
+
const ListPlugins: ArtalkPlugin[] = [
|
|
19
|
+
Fetch, Loading, Unread,
|
|
20
|
+
WithEditor, Count, SidebarBtn, UnreadBadge,
|
|
21
|
+
Dropdown, Goto, NoComment, Copyright,
|
|
22
|
+
TimeTicking, ErrorDialog, ReachBottom, GotoFirst
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
export { ListPlugins }
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ArtalkPlugin } from '~/types'
|
|
2
|
+
import * as Ui from '@/lib/ui'
|
|
3
|
+
|
|
4
|
+
export const Loading: ArtalkPlugin = (ctx) => {
|
|
5
|
+
ctx.on('list-fetch', (p) => {
|
|
6
|
+
const list = ctx.get('list')
|
|
7
|
+
|
|
8
|
+
if (p.offset === 0) // only show loading when fetch first page
|
|
9
|
+
Ui.setLoading(true, list.$el)
|
|
10
|
+
// else if not first page, show loading in paginator (code not there)
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
ctx.on('list-fetched', () => {
|
|
14
|
+
const list = ctx.get('list')
|
|
15
|
+
Ui.setLoading(false, list.$el)
|
|
16
|
+
})
|
|
17
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { ArtalkPlugin } from '~/types'
|
|
2
|
+
import * as Utils from '@/lib/utils'
|
|
3
|
+
import { sanitize } from '@/lib/sanitizer'
|
|
4
|
+
|
|
5
|
+
export const NoComment: ArtalkPlugin = (ctx) => {
|
|
6
|
+
ctx.on('list-loaded', (comments) => {
|
|
7
|
+
const list = ctx.get('list')!
|
|
8
|
+
|
|
9
|
+
// 无评论
|
|
10
|
+
const isNoComment = comments.length <= 0
|
|
11
|
+
let $noComment = list.getCommentsWrapEl().querySelector<HTMLElement>('.atk-list-no-comment')
|
|
12
|
+
|
|
13
|
+
if (isNoComment) {
|
|
14
|
+
if (!$noComment) {
|
|
15
|
+
$noComment = Utils.createElement('<div class="atk-list-no-comment"></div>')
|
|
16
|
+
|
|
17
|
+
// sanitize before set innerHTML
|
|
18
|
+
$noComment.innerHTML = sanitize(list.ctx.conf.noComment || list.ctx.$t('noComment'))
|
|
19
|
+
list.getCommentsWrapEl().appendChild($noComment)
|
|
20
|
+
}
|
|
21
|
+
} else {
|
|
22
|
+
$noComment?.remove()
|
|
23
|
+
}
|
|
24
|
+
})
|
|
25
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { ArtalkPlugin } from '~/types'
|
|
2
|
+
|
|
3
|
+
export const ReachBottom: ArtalkPlugin = (ctx) => {
|
|
4
|
+
const scrollEvtAt = document // TODO support ref ctx.conf
|
|
5
|
+
let observer: IntersectionObserver|null = null
|
|
6
|
+
|
|
7
|
+
const setupObserver = ($target: HTMLElement) => {
|
|
8
|
+
// eslint-disable-next-line compat/compat
|
|
9
|
+
observer = new IntersectionObserver(([entries]) => {
|
|
10
|
+
if (entries.isIntersecting) {
|
|
11
|
+
clearObserver()
|
|
12
|
+
ctx.trigger('list-reach-bottom')
|
|
13
|
+
}
|
|
14
|
+
}, {
|
|
15
|
+
root: scrollEvtAt
|
|
16
|
+
})
|
|
17
|
+
observer.observe($target)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const clearObserver = () => {
|
|
21
|
+
observer?.disconnect()
|
|
22
|
+
observer = null
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
ctx.on('list-loaded', () => {
|
|
26
|
+
clearObserver()
|
|
27
|
+
|
|
28
|
+
const list = ctx.get('list')
|
|
29
|
+
|
|
30
|
+
// use IntersectionObserver to detect reach bottom
|
|
31
|
+
const $target = list.getCommentsWrapEl().querySelector<HTMLElement>('.atk-comment-wrap:nth-last-child(3)')
|
|
32
|
+
if (!$target) return
|
|
33
|
+
|
|
34
|
+
// check IntersectionObserver support
|
|
35
|
+
if (!('IntersectionObserver' in window)) {
|
|
36
|
+
console.warn('IntersectionObserver api not supported')
|
|
37
|
+
return
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
setupObserver($target)
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
ctx.on('destroy', () => {
|
|
44
|
+
clearObserver()
|
|
45
|
+
})
|
|
46
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { ArtalkPlugin } from '~/types'
|
|
2
|
+
import $t from '@/i18n'
|
|
3
|
+
|
|
4
|
+
export const SidebarBtn: ArtalkPlugin = (ctx) => {
|
|
5
|
+
let $openSidebarBtn: HTMLElement|null = null
|
|
6
|
+
|
|
7
|
+
const syncByUser = () => {
|
|
8
|
+
if (!$openSidebarBtn) return
|
|
9
|
+
const user = ctx.get('user').data
|
|
10
|
+
|
|
11
|
+
// 已输入个人信息
|
|
12
|
+
if (!!user.nick && !!user.email) {
|
|
13
|
+
$openSidebarBtn.classList.remove('atk-hide')
|
|
14
|
+
|
|
15
|
+
// update button text (normal user or admin)
|
|
16
|
+
const $btnText = $openSidebarBtn.querySelector<HTMLElement>('.atk-text')
|
|
17
|
+
if ($btnText) $btnText.innerText = (!user.isAdmin) ? $t('msgCenter') : $t('ctrlCenter')
|
|
18
|
+
} else {
|
|
19
|
+
$openSidebarBtn.classList.add('atk-hide')
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
ctx.on('conf-loaded', () => {
|
|
24
|
+
const list = ctx.get('list')
|
|
25
|
+
|
|
26
|
+
$openSidebarBtn = list.$el.querySelector<HTMLElement>('[data-action="open-sidebar"]')
|
|
27
|
+
if (!$openSidebarBtn) return
|
|
28
|
+
|
|
29
|
+
$openSidebarBtn.onclick = () => { // use onclick rather than addEventListener to prevent duplicate event
|
|
30
|
+
ctx.showSidebar()
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
syncByUser()
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
ctx.on('user-changed', (user) => {
|
|
37
|
+
syncByUser()
|
|
38
|
+
})
|
|
39
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { ArtalkPlugin } from '~/types'
|
|
2
|
+
import * as Utils from '@/lib/utils'
|
|
3
|
+
|
|
4
|
+
/** 评论时间自动更新 */
|
|
5
|
+
export const TimeTicking: ArtalkPlugin = (ctx) => {
|
|
6
|
+
let timer: number|null = null
|
|
7
|
+
|
|
8
|
+
ctx.on('inited', () => {
|
|
9
|
+
timer = window.setInterval(() => {
|
|
10
|
+
const list = ctx.get('list')
|
|
11
|
+
|
|
12
|
+
list.$el.querySelectorAll<HTMLElement>('[data-atk-comment-date]').forEach(el => {
|
|
13
|
+
const date = el.getAttribute('data-atk-comment-date')
|
|
14
|
+
el.innerText = Utils.timeAgo(new Date(Number(date)), ctx.$t)
|
|
15
|
+
})
|
|
16
|
+
}, 30 * 1000) // 30s 更新一次
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
ctx.on('destroy', () => {
|
|
20
|
+
timer && window.clearInterval(timer)
|
|
21
|
+
})
|
|
22
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { ArtalkPlugin } from '~/types'
|
|
2
|
+
|
|
3
|
+
export const UnreadBadge: ArtalkPlugin = (ctx) => {
|
|
4
|
+
let $unreadBadge: HTMLElement|null = null
|
|
5
|
+
|
|
6
|
+
const showUnreadBadge = (count: number) => {
|
|
7
|
+
if (!$unreadBadge) return
|
|
8
|
+
|
|
9
|
+
if (count > 0) {
|
|
10
|
+
$unreadBadge.innerText = `${Number(count || 0)}`
|
|
11
|
+
$unreadBadge.style.display = 'block'
|
|
12
|
+
} else {
|
|
13
|
+
$unreadBadge.style.display = 'none'
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
ctx.on('conf-loaded', () => {
|
|
18
|
+
const list = ctx.get('list')
|
|
19
|
+
|
|
20
|
+
$unreadBadge = list.$el.querySelector<HTMLElement>('.atk-unread-badge')
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
ctx.on('unreads-updated', (unreads) => {
|
|
24
|
+
showUnreadBadge(unreads.length || 0)
|
|
25
|
+
})
|
|
26
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { ArtalkPlugin } from '~/types'
|
|
2
|
+
import * as Utils from '@/lib/utils'
|
|
3
|
+
|
|
4
|
+
export const Unread: ArtalkPlugin = (ctx) => {
|
|
5
|
+
ctx.on('comment-rendered', (comment) => {
|
|
6
|
+
// comment unread highlight
|
|
7
|
+
if (ctx.conf.listUnreadHighlight === true) {
|
|
8
|
+
const unreads = ctx.getData().getUnreads()
|
|
9
|
+
const notify = unreads.find(o => o.comment_id === comment.getID())
|
|
10
|
+
|
|
11
|
+
if (notify) {
|
|
12
|
+
// if comment contains in unread list
|
|
13
|
+
comment.getRender().setUnread(true)
|
|
14
|
+
comment.getRender().setOpenAction(() => {
|
|
15
|
+
window.open(notify.read_link)
|
|
16
|
+
|
|
17
|
+
// remove notify which has been read
|
|
18
|
+
ctx.getData().updateUnreads(unreads.filter(o => o.comment_id !== comment.getID()))
|
|
19
|
+
})
|
|
20
|
+
} else {
|
|
21
|
+
// comment not in unread list
|
|
22
|
+
comment.getRender().setUnread(false)
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
ctx.on('list-goto', (commentID) => {
|
|
28
|
+
const notifyKey = Utils.getQueryParam('atk_notify_key')
|
|
29
|
+
if (notifyKey) {
|
|
30
|
+
// mark as read
|
|
31
|
+
ctx.getApi().user.markRead(commentID, notifyKey)
|
|
32
|
+
.then(() => {
|
|
33
|
+
// remove from unread list
|
|
34
|
+
ctx.getData().updateUnreads(ctx.getData().getUnreads().filter(o => o.comment_id !== commentID))
|
|
35
|
+
})
|
|
36
|
+
}
|
|
37
|
+
})
|
|
38
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { ContextApi, ArtalkPlugin, PageData } from '~/types'
|
|
2
|
+
import $t from '@/i18n'
|
|
3
|
+
|
|
4
|
+
export const WithEditor: ArtalkPlugin = (ctx) => {
|
|
5
|
+
let $closeCommentBtn: HTMLElement|undefined
|
|
6
|
+
|
|
7
|
+
// on Artalk inited
|
|
8
|
+
// (after all components had mounted)
|
|
9
|
+
ctx.on('inited', () => {
|
|
10
|
+
const list = ctx.get('list')
|
|
11
|
+
|
|
12
|
+
$closeCommentBtn = list.$el.querySelector<HTMLElement>('[data-action="admin-close-comment"]')!
|
|
13
|
+
|
|
14
|
+
// bind editor close button click event
|
|
15
|
+
$closeCommentBtn.addEventListener('click', () => {
|
|
16
|
+
const page = ctx.getData().getPage()
|
|
17
|
+
if (!page) throw new Error('Page data not found')
|
|
18
|
+
|
|
19
|
+
page.admin_only = !page.admin_only
|
|
20
|
+
adminPageEditSave(ctx, page)
|
|
21
|
+
})
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
// on comment list loaded (it will include page data update)
|
|
25
|
+
ctx.on('page-loaded', (page) => {
|
|
26
|
+
const editor = ctx.get('editor')
|
|
27
|
+
|
|
28
|
+
// if page comment is closed
|
|
29
|
+
if (page?.admin_only === true) {
|
|
30
|
+
// then close editor
|
|
31
|
+
editor.getPlugs()?.getEvents().trigger('editor-close')
|
|
32
|
+
$closeCommentBtn && ($closeCommentBtn.innerText = $t('openComment'))
|
|
33
|
+
} else {
|
|
34
|
+
// the open editor
|
|
35
|
+
editor.getPlugs()?.getEvents().trigger('editor-open')
|
|
36
|
+
$closeCommentBtn && ($closeCommentBtn.innerText = $t('closeComment'))
|
|
37
|
+
}
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
ctx.on('list-loaded', (comments) => {
|
|
41
|
+
// 防止评论框被吞
|
|
42
|
+
ctx.editorResetState()
|
|
43
|
+
})
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** 管理员设置页面信息 */
|
|
47
|
+
function adminPageEditSave(ctx: ContextApi, page: PageData) {
|
|
48
|
+
ctx.editorShowLoading()
|
|
49
|
+
ctx.getApi().page.pageEdit(page)
|
|
50
|
+
.then((respPage) => {
|
|
51
|
+
ctx.getData().updatePage(respPage)
|
|
52
|
+
})
|
|
53
|
+
.catch(err => {
|
|
54
|
+
ctx.editorShowNotify(`${$t('editFail')}: ${err.msg || String(err)}`, 'e')
|
|
55
|
+
})
|
|
56
|
+
.finally(() => {
|
|
57
|
+
ctx.editorHideLoading()
|
|
58
|
+
})
|
|
59
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ArtalkPlugin } from '~/types'
|
|
2
|
+
import * as marked from '@/lib/marked'
|
|
3
|
+
|
|
4
|
+
export const Markdown: ArtalkPlugin = (ctx) => {
|
|
5
|
+
marked.initMarked()
|
|
6
|
+
|
|
7
|
+
ctx.on('conf-loaded', (conf) => {
|
|
8
|
+
if (conf.markedReplacers) marked.setReplacers(conf.markedReplacers)
|
|
9
|
+
})
|
|
10
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import type { ContextApi, ArtalkPlugin } from '~/types'
|
|
2
|
+
import Api from '../api'
|
|
3
|
+
|
|
4
|
+
export interface CountOptions {
|
|
5
|
+
getApi(): Api
|
|
6
|
+
|
|
7
|
+
pageKey: string
|
|
8
|
+
countEl: string
|
|
9
|
+
pvEl: string
|
|
10
|
+
|
|
11
|
+
/** 是否增加当前页面 PV 数 */
|
|
12
|
+
pvAdd?: boolean
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const PvCountWidget: ArtalkPlugin = (ctx: ContextApi) => {
|
|
16
|
+
ctx.on('conf-loaded', () => {
|
|
17
|
+
initCountWidget({
|
|
18
|
+
getApi: () => ctx.getApi(),
|
|
19
|
+
pageKey: ctx.conf.pageKey,
|
|
20
|
+
countEl: ctx.conf.countEl,
|
|
21
|
+
pvEl: ctx.conf.pvEl,
|
|
22
|
+
pvAdd: true,
|
|
23
|
+
})
|
|
24
|
+
})
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** 初始化评论数和 PV 数量展示元素 */
|
|
28
|
+
export async function initCountWidget(opt: CountOptions) {
|
|
29
|
+
// 评论数
|
|
30
|
+
if (opt.countEl && document.querySelector(opt.countEl)) {
|
|
31
|
+
refreshStatCount(opt, { query: 'page_comment', numEl: opt.countEl })
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// PV
|
|
35
|
+
const curtPagePvNum = opt.pvAdd ? await opt.getApi().page.pv() : NaN
|
|
36
|
+
if (opt.pvEl && document.querySelector(opt.pvEl)) {
|
|
37
|
+
refreshStatCount(opt, {
|
|
38
|
+
query: 'page_pv',
|
|
39
|
+
numEl: opt.pvEl,
|
|
40
|
+
data: { [opt.pageKey]: curtPagePvNum },
|
|
41
|
+
})
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
type CountData = { [pageKey: string]: number }
|
|
46
|
+
|
|
47
|
+
async function refreshStatCount(
|
|
48
|
+
opt: CountOptions,
|
|
49
|
+
args: {
|
|
50
|
+
query: 'page_pv' | 'page_comment'
|
|
51
|
+
numEl: string
|
|
52
|
+
data?: CountData
|
|
53
|
+
}
|
|
54
|
+
) {
|
|
55
|
+
let data: CountData = args.data || {}
|
|
56
|
+
|
|
57
|
+
// Get page keys from DOM
|
|
58
|
+
let pageKeys = Array.from(document.querySelectorAll(args.numEl))
|
|
59
|
+
.map((e) => e.getAttribute('data-page-key') || opt.pageKey)
|
|
60
|
+
.filter((e) => Number.isNaN(data[e])) // filter out keys that already have data
|
|
61
|
+
|
|
62
|
+
pageKeys = [...new Set(pageKeys)] // deduplicate
|
|
63
|
+
|
|
64
|
+
// Fetch count data from server
|
|
65
|
+
if (pageKeys.length > 0) {
|
|
66
|
+
const res = await opt.getApi().page.stat(args.query, pageKeys) as CountData
|
|
67
|
+
data = { ...data, ...res }
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
applyCountData(args.numEl, data, data[opt.pageKey])
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function applyCountData(selector: string, data: CountData, defaultCount: number) {
|
|
74
|
+
document.querySelectorAll(selector).forEach((el) => {
|
|
75
|
+
const pageKey = el.getAttribute('data-page-key')
|
|
76
|
+
const count = Number(pageKey ? (data[pageKey]) : defaultCount)
|
|
77
|
+
el.innerHTML = `${count}`
|
|
78
|
+
})
|
|
79
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { ArtalkPlugin } from '~/types'
|
|
2
|
+
import { version as ARTALK_VERSION } from '~/package.json'
|
|
3
|
+
import type List from '~/src/list/list'
|
|
4
|
+
import * as Ui from '@/lib/ui'
|
|
5
|
+
import * as Utils from '@/lib/utils'
|
|
6
|
+
import $t from '@/i18n'
|
|
7
|
+
|
|
8
|
+
let IgnoreVersionCheck = false
|
|
9
|
+
|
|
10
|
+
export const VersionCheck: ArtalkPlugin = (ctx) => {
|
|
11
|
+
ctx.on('conf-loaded', () => {
|
|
12
|
+
const list = ctx.get('list')
|
|
13
|
+
if (ctx.conf.apiVersion && ctx.conf.versionCheck && !IgnoreVersionCheck)
|
|
14
|
+
versionCheck(list, ARTALK_VERSION, ctx.conf.apiVersion)
|
|
15
|
+
})
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function versionCheck(list: List, feVer: string, beVer: string) {
|
|
19
|
+
const comp = Utils.versionCompare(feVer, beVer)
|
|
20
|
+
const sameVer = (comp === 0)
|
|
21
|
+
if (sameVer) return
|
|
22
|
+
|
|
23
|
+
const errEl = Utils.createElement(
|
|
24
|
+
`<div>请更新 Artalk ${comp < 0 ? $t('frontend') : $t('backend')}以获得完整体验 ` +
|
|
25
|
+
`(<a href="https://artalk.js.org/" target="_blank">帮助文档</a>)` +
|
|
26
|
+
`<br/><br/><span style="color: var(--at-color-meta);">` +
|
|
27
|
+
`当前版本:${$t('frontend')} ${feVer} / ${$t('backend')} ${beVer}` +
|
|
28
|
+
`</span><br/><br/></div>`)
|
|
29
|
+
const ignoreBtn = Utils.createElement('<span style="cursor:pointer">忽略</span>')
|
|
30
|
+
ignoreBtn.onclick = () => {
|
|
31
|
+
Ui.setError(list.$el.parentElement!, null)
|
|
32
|
+
IgnoreVersionCheck = true
|
|
33
|
+
list.ctx.fetch({ offset: 0 })
|
|
34
|
+
}
|
|
35
|
+
errEl.append(ignoreBtn)
|
|
36
|
+
Ui.setError(list.$el.parentElement!, errEl, '<span class="atk-warn-title">Artalk Warn</span>')
|
|
37
|
+
}
|