artalk 2.0.4 → 2.1.1

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.
Files changed (91) hide show
  1. package/README.md +7 -60
  2. package/dist/Artalk.css +1 -1
  3. package/dist/Artalk.es.js +5996 -1
  4. package/dist/Artalk.iife.js +266 -1
  5. package/dist/Artalk.js +266 -1
  6. package/package.json +3 -4
  7. package/src/api/index.ts +269 -0
  8. package/src/api/request.ts +125 -0
  9. package/src/artalk.ts +153 -0
  10. package/src/components/_component.ts.txt +10 -0
  11. package/src/components/action-btn.ts +169 -0
  12. package/src/components/admin/page-list.ts +194 -0
  13. package/src/components/admin/site-list-floater.ts +98 -0
  14. package/src/components/admin/site-list.ts +289 -0
  15. package/src/components/comment.ts +478 -0
  16. package/src/components/dialog.ts +63 -0
  17. package/src/components/editor-plugs/{_ExamplePlug.ts.txt → _example-plug.ts.txt} +0 -0
  18. package/src/components/editor-plugs/{EditorPlug.ts → editor-plug.ts} +3 -3
  19. package/src/components/editor-plugs/{EmoticonsPlug.less → emoticons-plug.less} +0 -0
  20. package/src/components/editor-plugs/{EmoticonsPlug.ts → emoticons-plug.ts} +9 -9
  21. package/src/components/editor-plugs/{PreviewPlug.less → preview-plug.less} +0 -0
  22. package/src/components/editor-plugs/{PreviewPlug.ts → preview-plug.ts} +10 -10
  23. package/src/components/{Editor.ts → editor.ts} +104 -102
  24. package/src/components/html/comment.html +2 -6
  25. package/src/components/html/list.html +0 -1
  26. package/src/components/html/sidebar.html +18 -4
  27. package/src/components/item-text-editor.ts +136 -0
  28. package/src/components/layer.ts +135 -0
  29. package/src/components/list-lite.ts +507 -0
  30. package/src/components/list.ts +172 -0
  31. package/src/components/pagination.ts +150 -0
  32. package/src/components/read-more-btn.ts +71 -0
  33. package/src/components/sidebar-view.ts +26 -0
  34. package/src/components/sidebar-views/comments-view.ts +66 -0
  35. package/src/components/sidebar-views/pages-view.ts +61 -0
  36. package/src/components/sidebar-views/setting-view.ts +21 -0
  37. package/src/components/sidebar-views/sites-view.ts +42 -0
  38. package/src/components/sidebar-views/transfer-view.ts +218 -0
  39. package/src/components/sidebar.ts +288 -0
  40. package/src/context.ts +42 -0
  41. package/src/default-conf.ts +42 -0
  42. package/src/lib/checker/admin-checker.ts +33 -0
  43. package/src/lib/checker/captcha-checker.ts +38 -0
  44. package/src/lib/checker/index.ts +134 -0
  45. package/src/lib/component.ts +3 -3
  46. package/src/lib/md5.js +6 -0
  47. package/src/lib/ui.ts +64 -74
  48. package/src/lib/user.ts +2 -8
  49. package/src/lib/utils.ts +30 -11
  50. package/src/main.ts +1 -1
  51. package/src/style/_color.less +8 -2
  52. package/src/style/_extend.less +81 -0
  53. package/src/style/_variables.less +3 -0
  54. package/src/style/comment.less +60 -24
  55. package/src/style/editor.less +3 -0
  56. package/src/style/item-text-editor.less +35 -0
  57. package/src/style/list.less +51 -32
  58. package/src/style/main.less +75 -1
  59. package/src/style/page-list.less +50 -0
  60. package/src/style/pagination.less +58 -0
  61. package/src/style/sidebar.less +274 -199
  62. package/src/style/site-list.less +208 -0
  63. package/tsconfig.json +27 -0
  64. package/types/artalk-config.d.ts +68 -51
  65. package/types/artalk-data.d.ts +38 -1
  66. package/types/event.d.ts +73 -0
  67. package/types/index.d.ts +1 -6
  68. package/.github/workflows/npm.yml +0 -31
  69. package/.github/workflows/pages.yml +0 -30
  70. package/dist/src/Event.d.ts +0 -59
  71. package/dist/types/artalk-config.d.ts +0 -79
  72. package/dist/types/artalk-data.d.ts +0 -153
  73. package/dist/types/global.d.ts +0 -1
  74. package/dist/types/index.d.ts +0 -8
  75. package/dist/types/vite-env.d.ts +0 -1
  76. package/example/pjax/res1.html +0 -41
  77. package/example/pjax/res2.html +0 -41
  78. package/src/Artalk.ts +0 -160
  79. package/src/Constant.ts +0 -6
  80. package/src/Context.ts +0 -34
  81. package/src/Event.d.ts +0 -59
  82. package/src/components/Comment.ts +0 -402
  83. package/src/components/Layer.ts +0 -138
  84. package/src/components/List.ts +0 -162
  85. package/src/components/ListLite.ts +0 -407
  86. package/src/components/Sidebar.ts +0 -141
  87. package/src/components/sidebar-views/AdminView.ts +0 -523
  88. package/src/components/sidebar-views/MessageView.ts +0 -48
  89. package/src/components/sidebar-views/SidebarView.ts +0 -24
  90. package/src/lib/api.ts +0 -380
  91. package/src/lib/checker.ts +0 -164
@@ -1,402 +0,0 @@
1
- import '../style/comment.less'
2
-
3
- import Context from '../Context'
4
- import Component from '../lib/component'
5
- import * as Utils from '../lib/utils'
6
- import * as Ui from '../lib/ui'
7
- import UADetect from '../lib/detect'
8
- import { CommentData } from '~/types/artalk-data'
9
- import CommentHTML from './html/comment.html?raw'
10
- import Api from '../lib/api'
11
-
12
- export default class Comment extends Component {
13
- public data: CommentData
14
-
15
- public mainEl!: HTMLElement
16
- public bodyEl!: HTMLElement
17
- public contentEl!: HTMLElement
18
- public childrenEl!: HTMLElement|null
19
- public actionsEl!: HTMLElement
20
-
21
- public parent: Comment|null
22
- public nestedNum: number
23
- private readonly maxNestingNum = 3 // 最多嵌套层数
24
- public children: Comment[] = []
25
-
26
- public replyTo?: CommentData // 回复对象(flatMode 用)
27
-
28
- public afterRender?: () => void
29
-
30
- private unread = false
31
- private openable = false
32
- private openURL?: string
33
- public openEvt?: () => void
34
-
35
- constructor (ctx: Context, data: CommentData) {
36
- super(ctx)
37
-
38
- this.data = { ...data }
39
- this.data.date = this.data.date.replace(/-/g, '/') // 解决 Safari 日期解析 NaN 问题
40
-
41
- this.parent = null
42
- this.nestedNum = 1 // 现在已嵌套 n 层
43
- }
44
-
45
- public renderElem () {
46
- this.el = Utils.createElement(CommentHTML)
47
- this.mainEl = this.el.querySelector('.atk-comment-main')!
48
- this.bodyEl = this.el.querySelector('.atk-body')!
49
- this.contentEl = this.bodyEl.querySelector('.atk-content')!
50
- this.actionsEl = this.el.querySelector('.atk-comment-actions')!
51
- this.childrenEl = null
52
-
53
- // class style
54
- if (this.unread) this.el.classList.add('atk-unread')
55
- else this.el.classList.remove('atk-unread')
56
-
57
- if (this.openable) {
58
- this.el.classList.add('atk-openable')
59
- } else {
60
- this.el.classList.remove('atk-openable')
61
- }
62
-
63
- this.el.addEventListener('click', (evt) => {
64
- if (this.openable && this.openURL) {
65
- evt.preventDefault()
66
- window.open(this.openURL)
67
- }
68
- if (this.openEvt)
69
- this.openEvt()
70
- })
71
-
72
- // 填入内容
73
- this.el.setAttribute('data-comment-id', `${this.data.id}`)
74
- this.el.querySelector('.atk-avatar a')!.setAttribute('href', this.data.link)
75
- this.el.querySelector('.atk-avatar img')!.setAttribute('src', this.getGravatarUrl())
76
-
77
- const nickEl = this.el.querySelector<HTMLLinkElement>('.atk-nick > a')!
78
- nickEl.innerText = this.data.nick
79
- nickEl.href = this.data.link
80
-
81
- const badgeEl = this.el.querySelector<HTMLElement>('.atk-badge')!
82
- if (this.data.badge_name) {
83
- badgeEl.innerText = this.data.badge_name
84
- if (this.data.badge_color)
85
- badgeEl.style.backgroundColor = this.data.badge_color
86
- } else {
87
- badgeEl.remove()
88
- }
89
-
90
- const dateEL = this.el.querySelector<HTMLElement>('.atk-date')!
91
- dateEL.innerText = this.getDateFormatted()
92
- dateEL.setAttribute('data-atk-comment-date', String(+new Date(this.data.date)))
93
- this.el.querySelector<HTMLElement>('.atk-ua.ua-browser')!.innerText = this.getUserUaBrowser()
94
- this.el.querySelector<HTMLElement>('.atk-ua.ua-os')!.innerText = this.getUserUaOS()
95
-
96
- // 内容 & 折叠
97
- if (!this.data.is_collapsed) {
98
- this.contentEl.innerHTML = this.getContentMarked()
99
- } else {
100
- this.contentEl.classList.add('atk-hide', 'atk-type-collapsed')
101
- const collapsedInfoEl = Utils.createElement(`
102
- <div class="atk-collapsed">
103
- <span class="atk-text">该评论已被系统或管理员折叠</span>
104
- <span class="atk-show-btn">查看内容</span>
105
- </div>`)
106
- this.bodyEl.insertAdjacentElement('beforeend', collapsedInfoEl)
107
-
108
- const contentShowBtn = collapsedInfoEl.querySelector('.atk-show-btn')!
109
- contentShowBtn.addEventListener('click', (e) => {
110
- if (this.contentEl.classList.contains('atk-hide')) {
111
- this.contentEl.innerHTML = this.getContentMarked()
112
- this.contentEl.classList.remove('atk-hide')
113
- Ui.playFadeInAnim(this.contentEl)
114
- contentShowBtn.innerHTML = '收起内容'
115
- } else {
116
- this.contentEl.innerHTML = ''
117
- this.contentEl.classList.add('atk-hide')
118
- contentShowBtn.innerHTML = '查看内容'
119
- }
120
-
121
- e.stopPropagation() // 防止穿透
122
- })
123
- }
124
-
125
- // 显示回复的对象
126
- if (this.replyTo) {
127
- const replyToEl = Utils.createElement(`
128
- <div class="atk-reply-to">
129
- <div class="atk-meta">回复 <span class="atk-nick"></span>:</div>
130
- <div class="atk-content"></div>
131
- </div>`)
132
- replyToEl.querySelector<HTMLElement>('.atk-nick')!.innerText = `@${this.replyTo.nick}`
133
- replyToEl.querySelector<HTMLElement>('.atk-content')!.innerHTML = Utils.marked(this.ctx, this.replyTo.content)
134
- this.bodyEl.prepend(replyToEl)
135
- }
136
-
137
- // 显示待审核状态
138
- if (this.data.is_pending) {
139
- const pendingEl = Utils.createElement(`<div class="atk-pending">审核中,仅本人可见。</div>`)
140
- this.bodyEl.prepend(pendingEl)
141
- }
142
-
143
- this.initActionBtn()
144
-
145
- if (this.afterRender) this.afterRender()
146
-
147
- return this.el
148
- }
149
-
150
- private eachComment (commentList: Comment[], action: (comment: Comment, levelList: Comment[]) => boolean|void) {
151
- if (commentList.length === 0) return
152
- commentList.every((item) => {
153
- if (action(item, commentList) === false) return false
154
- this.eachComment(item.getChildren(), action)
155
- return true
156
- })
157
- }
158
-
159
- public refreshUI () {
160
- const originalEl = this.el
161
- const newEl = this.renderElem()
162
- originalEl.replaceWith(newEl) // 替换 document 中的的 elem
163
- this.playFadeInAnim()
164
-
165
- // 重建子评论元素
166
- this.eachComment(this.children, (child) => {
167
- child.parent?.getChildrenEl().appendChild(child.renderElem())
168
- child.playFadeInAnim()
169
- })
170
- }
171
-
172
- initActionBtn () {
173
- // 赞同按钮
174
- const voteBtnUp = Utils.createElement(`<span></span>`)
175
- const voteBtnDown = Utils.createElement(`<span></span>`)
176
- this.actionsEl.append(voteBtnUp)
177
- this.actionsEl.append(voteBtnDown)
178
-
179
- const refreshVote = () => {
180
- voteBtnUp.innerText = `赞同 (${this.data.vote_up || 0})`
181
- voteBtnDown.innerText = `反对 (${this.data.vote_down || 0})`
182
- }
183
- refreshVote()
184
-
185
- voteBtnUp.onclick = () => {
186
- new Api(this.ctx).vote(this.data.id, "comment_up")
187
- .then((num) => {
188
- this.data.vote_up = num
189
- refreshVote()
190
- })
191
- .catch((err) => {
192
- window.alert(`投票失败:${err.msg || String(err)}`)
193
- })
194
- }
195
- voteBtnDown.onclick = () => {
196
- new Api(this.ctx).vote(this.data.id, "comment_down")
197
- .then((num) => {
198
- this.data.vote_down = num
199
- refreshVote()
200
- })
201
- .catch((err) => {
202
- window.alert(`投票失败:${err.msg || String(err)}`)
203
- })
204
- }
205
-
206
- // 绑定回复按钮事件
207
- if (this.data.is_allow_reply) {
208
- const replyBtn = Utils.createElement(`<span data-atk-action="comment-reply">回复</span>`)
209
- this.actionsEl.append(replyBtn)
210
- replyBtn.addEventListener('click', (e) => {
211
- this.ctx.dispatchEvent('editor-reply', this.data)
212
- e.stopPropagation() // 防止穿透
213
- })
214
- }
215
-
216
- // 管理员操作按钮
217
-
218
- // 绑定折叠按钮事件
219
- const collapseBtn = Utils.createElement(`<span atk-only-admin-show>${this.data.is_collapsed ? '取消折叠' : '折叠'}</span>`)
220
- this.actionsEl.append(collapseBtn)
221
- collapseBtn.addEventListener('click', (e) => {
222
- this.adminEdit('collapsed', collapseBtn)
223
- e.stopPropagation() // 防止穿透
224
- })
225
-
226
- // 绑定待审核按钮事件
227
- const pendingBtn = Utils.createElement(`<span atk-only-admin-show>${this.data.is_pending ? '待审' : '已审'}</span>`)
228
- this.actionsEl.append(pendingBtn)
229
- pendingBtn.addEventListener('click', (e) => {
230
- this.adminEdit('pending', pendingBtn)
231
- e.stopPropagation() // 防止穿透
232
- })
233
-
234
- // 绑定删除按钮事件
235
- const delBtn = Utils.createElement(`<span atk-only-admin-show>删除</span>`)
236
- this.actionsEl.append(delBtn)
237
- delBtn.addEventListener('click', (e) => {
238
- this.adminDelete(delBtn)
239
- e.stopPropagation() // 防止穿透
240
- })
241
- }
242
-
243
- getIsRoot () {
244
- return this.parent === null
245
- }
246
-
247
- getChildren () {
248
- return this.children
249
- }
250
-
251
- putChild (childC: Comment) {
252
- childC.parent = this
253
- childC.nestedNum = this.nestedNum + 1 // 嵌套层数 +1
254
- this.children.push(childC)
255
-
256
- this.getChildrenEl().appendChild(childC.getEl())
257
- childC.playFadeInAnim()
258
- }
259
-
260
- getChildrenEl () {
261
- if (this.childrenEl === null) {
262
- // console.log(this.nestedNum)
263
- if (this.nestedNum < this.maxNestingNum) {
264
- this.childrenEl = Utils.createElement('<div class="atk-comment-children"></div>')
265
- this.mainEl.appendChild(this.childrenEl)
266
- } else if (this.parent) {
267
- this.childrenEl = this.parent.getChildrenEl()
268
- }
269
- }
270
- return this.childrenEl
271
- }
272
-
273
- getParent () {
274
- return this.parent
275
- }
276
-
277
- getEl () {
278
- return this.el
279
- }
280
-
281
- getData () {
282
- return this.data
283
- }
284
-
285
- getGravatarUrl () {
286
- return `${this.ctx.conf.gravatar?.cdn || ''}${this.data.email_encrypted}?d=${encodeURIComponent(this.ctx.conf.defaultAvatar || '')}&s=80`
287
- }
288
-
289
- getContentMarked () {
290
- return Utils.marked(this.ctx, this.data.content)
291
- }
292
-
293
- getDateFormatted () {
294
- return Utils.timeAgo(new Date(this.data.date))
295
- }
296
-
297
- getUserUaBrowser () {
298
- const info = UADetect(this.data.ua)
299
- return `${info.browser} ${info.version}`
300
- }
301
-
302
- getUserUaOS () {
303
- const info = UADetect(this.data.ua)
304
- return `${info.os} ${info.osVersion}`
305
- }
306
-
307
- /** 渐出动画 */
308
- playFadeInAnim () {
309
- Ui.playFadeInAnim(this.el)
310
- }
311
-
312
- /** 管理员 - 评论折叠 */
313
- adminEdit (type: 'collapsed'|'pending', btnElem: HTMLElement) {
314
- if (btnElem.classList.contains('atk-in-process')) return // 若正在折叠中
315
- const btnTextOrg = btnElem.innerText
316
-
317
- btnElem.classList.add('atk-in-process')
318
- btnElem.innerText = '修改中...'
319
-
320
- if (type === 'collapsed') {
321
- this.data.is_collapsed = !this.data.is_collapsed
322
- } else if (type === 'pending') {
323
- this.data.is_pending = !this.data.is_pending
324
- }
325
-
326
- new Api(this.ctx).commentEdit(this.data).then((comment) => {
327
- btnElem.classList.remove('atk-in-process')
328
- this.data = comment
329
- this.refreshUI()
330
- Ui.playFadeInAnim(this.bodyEl)
331
- this.ctx.dispatchEvent('list-refresh-ui')
332
- }).catch((err) => {
333
- console.error(err)
334
- btnElem.classList.add('atk-error')
335
- btnElem.innerText = '修改失败'
336
- setTimeout(() => {
337
- btnElem.innerText = btnTextOrg
338
- btnElem.classList.remove('atk-error')
339
- btnElem.classList.remove('atk-in-process')
340
- }, 2000)
341
- })
342
- }
343
-
344
- public onDelete?: (comment: Comment) => void
345
-
346
- /** 管理员 - 评论删除 */
347
- adminDelete (btnElem: HTMLElement) {
348
- if (btnElem.classList.contains('atk-in-process')) return // 若正在删除中
349
-
350
- // 删除确认
351
- const btnClicked = Number(btnElem.getAttribute('data-btn-clicked') || 1)
352
- if (btnClicked < 2) {
353
- if (btnClicked === 1) {
354
- const btnTextOrg = btnElem.innerText
355
- btnElem.innerText = '确认删除'
356
- setTimeout(() => {
357
- btnElem.innerText = btnTextOrg
358
- btnElem.setAttribute('data-btn-clicked', '')
359
- }, 2000)
360
- btnElem.setAttribute('data-btn-clicked', String(btnClicked+1))
361
- }
362
- return
363
- }
364
- const btnTextOrg = btnElem.innerText
365
-
366
- btnElem.classList.add('atk-in-process')
367
- btnElem.innerText = '删除中...'
368
- new Api(this.ctx).commentDel(this.data.id, this.data.site_name)
369
- .then(() => {
370
- btnElem.innerText = btnTextOrg
371
- btnElem.classList.remove('atk-in-process')
372
- if (this.onDelete) this.onDelete(this)
373
- })
374
- .catch((e) => {
375
- console.error(e)
376
- btnElem.classList.add('atk-error')
377
- btnElem.innerText = '删除失败'
378
- setTimeout(() => {
379
- btnElem.innerText = btnTextOrg
380
- btnElem.classList.remove('atk-error')
381
- btnElem.classList.remove('atk-in-process')
382
- }, 2000)
383
- })
384
- }
385
-
386
- public setUnread (val: boolean) {
387
- this.unread = val
388
- if (this.unread) this.el.classList.add('atk-unread')
389
- else this.el.classList.remove('atk-unread')
390
- }
391
-
392
- public setOpenURL (url: string) {
393
- if (!url) {
394
- this.openable = false
395
- this.el.classList.remove('atk-openable')
396
- }
397
-
398
- this.openable = true
399
- this.openURL = url
400
- this.el.classList.add('atk-openable')
401
- }
402
- }
@@ -1,138 +0,0 @@
1
- import Context from '../Context'
2
- import Component from '../lib/component'
3
- import Constant from '../Constant'
4
- import * as Utils from '../lib/utils'
5
- import * as Ui from '../lib/ui'
6
-
7
- export function GetLayerWrap (ctx: Context): { wrapEl: HTMLElement, maskEl: HTMLElement } {
8
- let wrapEl = document.querySelector<HTMLElement>(`.atk-layer-wrap#ctx-${ctx.cid}`)
9
- if (!wrapEl) {
10
- wrapEl = Utils.createElement(
11
- `<div class="atk-layer-wrap" id="ctx-${ctx.cid}" style="display: none;"><div class="atk-layer-mask"></div></div>`
12
- )
13
- document.body.appendChild(wrapEl)
14
- }
15
-
16
- const maskEl = wrapEl.querySelector<HTMLElement>('.atk-layer-mask')!
17
-
18
- // dark mode
19
- if (wrapEl) {
20
- if (ctx.conf.darkMode) {
21
- wrapEl.classList.add(Constant.DARK_MODE_CLASSNAME)
22
- } else {
23
- wrapEl.classList.remove(Constant.DARK_MODE_CLASSNAME)
24
- }
25
- }
26
-
27
- return { wrapEl, maskEl }
28
- }
29
-
30
- export default function BuildLayer (ctx: Context, name: string, el?: HTMLElement) {
31
- return new Layer(ctx, name, el)
32
- }
33
-
34
- export class Layer extends Component {
35
- private name: string
36
- private wrapEl: HTMLElement
37
- private maskEl: HTMLElement
38
-
39
- private maskClickHideEnable: boolean = true
40
-
41
- constructor (ctx: Context, name: string, el?: HTMLElement) {
42
- super(ctx)
43
-
44
- this.name = name
45
- const { wrapEl, maskEl } = GetLayerWrap(ctx)
46
- this.wrapEl = wrapEl
47
- this.maskEl = maskEl
48
-
49
- this.el = this.wrapEl.querySelector(`[data-layer-name="${name}"].atk-layer-item`)!
50
- if (this.el === null) {
51
- // 若传递 layer 元素为空
52
- if (!el) {
53
- this.el = Utils.createElement()
54
- this.el.classList.add('atk-layer-item')
55
- } else {
56
- this.el = el
57
- }
58
- }
59
- this.el.setAttribute('data-layer-name', name)
60
- this.el.style.display = 'none'
61
-
62
- // 添加到 layers wrap 中
63
- this.wrapEl.append(this.el)
64
- }
65
-
66
- getName () {
67
- return this.name
68
- }
69
-
70
- getWrapEl () {
71
- return this.wrapEl
72
- }
73
-
74
- getEl () {
75
- return this.el
76
- }
77
-
78
- private static hideTimeoutList: number[] = []
79
-
80
- show () {
81
- Layer.hideTimeoutList.forEach(item => {
82
- clearTimeout(item)
83
- })
84
- Layer.hideTimeoutList = []
85
-
86
- this.wrapEl.style.display = 'block'
87
- this.maskEl.style.display = 'block'
88
- this.maskEl.classList.add('atk-fade-in')
89
- this.el.style.display = ''
90
-
91
- this.maskEl.onclick = () => {
92
- if (this.maskClickHideEnable) this.hide()
93
- }
94
-
95
- document.body.style.overflow = 'hidden'
96
- }
97
-
98
- hide () {
99
- Layer.hideTimeoutList.push(window.setTimeout(() => {
100
- this.wrapEl.style.display = 'none'
101
- document.body.style.overflow = ''
102
- }, 450))
103
-
104
- this.wrapEl.classList.add('atk-fade-out')
105
- Layer.hideTimeoutList.push(window.setTimeout(() => {
106
- this.wrapEl.style.display = 'none'
107
- this.wrapEl.classList.remove('atk-fade-out')
108
- }, 200))
109
-
110
- this.el.style.display = 'none'
111
- }
112
-
113
- setMaskClickHide (enable: boolean) {
114
- this.maskClickHideEnable = enable
115
- }
116
-
117
- /** 销毁 - 无动画 */
118
- disposeNow () {
119
- document.body.style.overflow = ''
120
- this.el.remove()
121
- // this.el dispose
122
- this.checkCleanLayer()
123
- }
124
-
125
- /** 销毁 */
126
- dispose () {
127
- this.hide()
128
- this.el.remove()
129
- // this.el dispose
130
- this.checkCleanLayer()
131
- }
132
-
133
- checkCleanLayer () {
134
- if (this.getWrapEl().querySelectorAll('.atk-layer-item').length === 0) {
135
- this.wrapEl.style.display = 'none'
136
- }
137
- }
138
- }
@@ -1,162 +0,0 @@
1
- import '../style/list.less'
2
-
3
- import Context from '../Context'
4
- import * as Utils from '../lib/utils'
5
- import * as Ui from '../lib/ui'
6
- import Api from '../lib/api'
7
- import ListHTML from './html/list.html?raw'
8
- import ListLite from './ListLite'
9
- import { ListData } from '~/types/artalk-data'
10
-
11
- export default class List extends ListLite {
12
- private closeCommentBtnEl!: HTMLElement
13
- private openSidebarBtnEl!: HTMLElement
14
- private openAdminPanelBtnEl!: HTMLElement
15
- private unreadBadgeEl!: HTMLElement
16
-
17
- constructor (ctx: Context) {
18
- super(ctx)
19
-
20
- const el = Utils.createElement(ListHTML)
21
- el.querySelector('.atk-list-body')!.append(this.el)
22
- this.el = el
23
-
24
- // 操作按钮
25
- this.initListActionBtn()
26
-
27
- this.el.querySelector<HTMLElement>('.atk-copyright')!.innerHTML = `Powered By <a href="https://artalk.js.org" target="_blank" title="Artalk v${ARTALK_VERSION}">Artalk</a>`
28
-
29
- this.ctx.addEventListener('list-reload', () => (this.reqComments(0))) // 刷新评论
30
- this.ctx.addEventListener('list-clear', () => (this.clearComments())) // 清空评论
31
- this.ctx.addEventListener('list-refresh-ui', () => (this.refreshUI()))
32
- this.ctx.addEventListener('list-import', (data) => (this.importComments(data)))
33
- this.ctx.addEventListener('list-insert', (data) => (this.insertComment(data)))
34
- this.ctx.addEventListener('list-comment-del', (comment) => (this.deleteComment(comment.id)))
35
- this.ctx.addEventListener('list-update-data', (updateData) => { updateData(this.data);this.refreshUI() } )
36
- this.ctx.addEventListener('unread-update', (data) => (this.showUnreadBadge(data.notifies?.length || 0)))
37
- }
38
-
39
- /** 刷新界面 */
40
- public refreshUI () {
41
- super.refreshUI()
42
-
43
- this.el.querySelector<HTMLElement>('.atk-comment-count-num')!.innerText = String(this.getListCommentCount())
44
-
45
- // 已输入个人信息
46
- if (!!this.ctx.user.data.nick && !!this.ctx.user.data.email) {
47
- this.openSidebarBtnEl.classList.remove('atk-hide')
48
- } else {
49
- this.openSidebarBtnEl.classList.add('atk-hide')
50
- }
51
-
52
- // 仅管理员显示控制
53
- this.ctx.dispatchEvent('check-admin-show-el')
54
-
55
- // 关闭评论
56
- if (!!this.data && !!this.data.page && this.data.page.admin_only === true) {
57
- this.ctx.dispatchEvent('editor-close-comment')
58
- this.closeCommentBtnEl.innerHTML = '打开评论'
59
- } else {
60
- this.ctx.dispatchEvent('editor-open-comment')
61
- this.closeCommentBtnEl.innerHTML = '关闭评论'
62
- }
63
- }
64
-
65
- public onLoad(data: ListData) {
66
- super.onLoad(data)
67
-
68
- // 检测锚点跳转
69
- this.checkGoToCommentByUrlHash()
70
- }
71
-
72
- /** 跳到评论项位置 - 根据 `location.hash` */
73
- public async checkGoToCommentByUrlHash () {
74
- let commentId = Number(Utils.getQueryParam('atk_comment')) // same as backend GetReplyLink()
75
- if (!commentId) {
76
- const match = window.location.hash.match(/#atk-comment-([0-9]+)/)
77
- if (!match || !match[1] || Number.isNaN(Number(match[1]))) return
78
- commentId = Number(match[1])
79
- }
80
- if (!commentId) return
81
-
82
- const comment = this.findComment(commentId)
83
- if (!comment) { // 若找不到评论
84
- if (this.hasMoreComments) { // 阅读更多,并重试
85
- await this.readMore()
86
- }
87
- }
88
-
89
- if (!comment) return
90
-
91
- Ui.scrollIntoView(comment.getEl(), false)
92
- setTimeout(() => {
93
- comment.getEl().classList.add('atk-flash-once')
94
-
95
- // 已阅 API
96
- const notifyKey = Utils.getQueryParam('atk_notify_key')
97
- if (notifyKey) {
98
- new Api(this.ctx).markRead(notifyKey)
99
- .then(() => {
100
- this.unread = this.unread.filter(o => o.comment_id !== comment.data.id)
101
- this.ctx.dispatchEvent('unread-update', {
102
- notifies: this.unread
103
- })
104
- })
105
- }
106
- }, 800)
107
- }
108
-
109
- private initListActionBtn () {
110
- // 侧边栏呼出按钮
111
- this.openSidebarBtnEl = this.el.querySelector('[data-action="open-sidebar"]')!
112
- this.openSidebarBtnEl.addEventListener('click', () => {
113
- this.ctx.dispatchEvent('sidebar-show', { viewName: 'message' })
114
- })
115
-
116
- // 控制台呼出
117
- this.openAdminPanelBtnEl = this.el.querySelector('[data-action="open-admin-panel"]')!
118
- this.openAdminPanelBtnEl.addEventListener('click', () => {
119
- this.ctx.dispatchEvent('sidebar-show', { viewName: 'admin' })
120
- })
121
-
122
-
123
- // 关闭评论按钮
124
- this.closeCommentBtnEl = this.el.querySelector('[data-action="admin-close-comment"]')!
125
- this.closeCommentBtnEl.addEventListener('click', () => {
126
- if (!this.data) return
127
-
128
- this.data.page.admin_only = !this.data.page.admin_only
129
- this.adminPageEditSave()
130
- })
131
-
132
- this.unreadBadgeEl = this.el.querySelector('.atk-unread-badge')!
133
- }
134
-
135
- /** 管理员设置页面信息 */
136
- public adminPageEditSave () {
137
- if (!this.data || !this.data.page) return
138
-
139
- this.ctx.dispatchEvent('editor-show-loading')
140
- new Api(this.ctx).pageEdit(this.data.page)
141
- .then((page) => {
142
- if (this.data)
143
- this.data.page = { ...page }
144
- this.refreshUI()
145
- })
146
- .catch(err => {
147
- this.ctx.dispatchEvent('editor-notify', { msg: `修改页面数据失败:${err.msg || String(err)}`, type: 'e'})
148
- })
149
- .finally(() => {
150
- this.ctx.dispatchEvent('editor-hide-loading')
151
- })
152
- }
153
-
154
- public showUnreadBadge (count: number) {
155
- if (count > 0) {
156
- this.unreadBadgeEl.innerText = `${Number(count || 0)}`
157
- this.unreadBadgeEl.style.display = 'block'
158
- } else {
159
- this.unreadBadgeEl.style.display = 'none'
160
- }
161
- }
162
- }