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/comment/render.ts
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import '../style/comment.less'
|
|
2
|
-
|
|
3
1
|
import * as Utils from '../lib/utils'
|
|
4
2
|
import * as Ui from '../lib/ui'
|
|
5
3
|
import ActionBtn from '../components/action-btn'
|
|
@@ -49,7 +47,7 @@ export default class CommentRender {
|
|
|
49
47
|
this.renderReplyAt()
|
|
50
48
|
this.renderReplyTo()
|
|
51
49
|
this.renderPending()
|
|
52
|
-
this.
|
|
50
|
+
this.renderActions()
|
|
53
51
|
|
|
54
52
|
this.recoveryChildrenWrap()
|
|
55
53
|
|
|
@@ -63,7 +61,7 @@ export default class CommentRender {
|
|
|
63
61
|
$avatarImg.src = this.comment.getGravatarURL()
|
|
64
62
|
if (this.data.link) {
|
|
65
63
|
const $avatarA = Utils.createElement<HTMLLinkElement>('<a target="_blank" rel="noreferrer noopener nofollow"></a>')
|
|
66
|
-
$avatarA.href = this.data.link
|
|
64
|
+
$avatarA.href = Utils.isValidURL(this.data.link) ? this.data.link : `https://${this.data.link}`
|
|
67
65
|
$avatarA.append($avatarImg)
|
|
68
66
|
$avatar.append($avatarA)
|
|
69
67
|
} else {
|
|
@@ -73,17 +71,26 @@ export default class CommentRender {
|
|
|
73
71
|
|
|
74
72
|
/** 初始化 - 评论信息 */
|
|
75
73
|
private renderHeader() {
|
|
74
|
+
this.renderHeader_Nick()
|
|
75
|
+
this.renderHeader_VerifyBadge()
|
|
76
|
+
this.renderHeader_Date()
|
|
77
|
+
this.renderHeader_UABadge()
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
private renderHeader_Nick() {
|
|
76
81
|
this.$headerNick = this.$el.querySelector<HTMLElement>('.atk-nick')!
|
|
77
82
|
|
|
78
83
|
if (this.data.link) {
|
|
79
84
|
const $nickA = Utils.createElement<HTMLLinkElement>('<a target="_blank" rel="noreferrer noopener nofollow"></a>')
|
|
80
85
|
$nickA.innerText = this.data.nick
|
|
81
|
-
$nickA.href = this.data.link
|
|
86
|
+
$nickA.href = Utils.isValidURL(this.data.link) ? this.data.link : `https://${this.data.link}`
|
|
82
87
|
this.$headerNick.append($nickA)
|
|
83
88
|
} else {
|
|
84
89
|
this.$headerNick.innerText = this.data.nick
|
|
85
90
|
}
|
|
91
|
+
}
|
|
86
92
|
|
|
93
|
+
private renderHeader_VerifyBadge() {
|
|
87
94
|
this.$headerBadgeWrap = this.$el.querySelector<HTMLElement>('.atk-badge-wrap')!
|
|
88
95
|
this.$headerBadgeWrap.innerHTML = ''
|
|
89
96
|
|
|
@@ -100,11 +107,15 @@ export default class CommentRender {
|
|
|
100
107
|
const $pinnedBadge = Utils.createElement(`<span class="atk-pinned-badge">${this.ctx.$t('pin')}</span>`) // 置顶徽章
|
|
101
108
|
this.$headerBadgeWrap.append($pinnedBadge)
|
|
102
109
|
}
|
|
110
|
+
}
|
|
103
111
|
|
|
112
|
+
private renderHeader_Date() {
|
|
104
113
|
const $date = this.$el.querySelector<HTMLElement>('.atk-date')!
|
|
105
114
|
$date.innerText = this.comment.getDateFormatted()
|
|
106
115
|
$date.setAttribute('data-atk-comment-date', String(+new Date(this.data.date)))
|
|
116
|
+
}
|
|
107
117
|
|
|
118
|
+
private renderHeader_UABadge() {
|
|
108
119
|
if (this.ctx.conf.uaBadge) {
|
|
109
120
|
let $uaWrap = this.$header.querySelector('atk-ua-wrap')
|
|
110
121
|
if (!$uaWrap) {
|
|
@@ -115,8 +126,9 @@ export default class CommentRender {
|
|
|
115
126
|
$uaWrap.innerHTML = ''
|
|
116
127
|
const $uaBrowser = Utils.createElement(`<span class="atk-ua ua-browser"></span>`)
|
|
117
128
|
const $usOS = Utils.createElement(`<span class="atk-ua ua-os"></span>`)
|
|
118
|
-
|
|
119
|
-
$
|
|
129
|
+
const uaInfo = this.comment.getUserUA()
|
|
130
|
+
$uaBrowser.innerText = uaInfo.browser
|
|
131
|
+
$usOS.innerText = uaInfo.os
|
|
120
132
|
$uaWrap.append($uaBrowser)
|
|
121
133
|
$uaWrap.append($usOS)
|
|
122
134
|
}
|
|
@@ -196,39 +208,55 @@ export default class CommentRender {
|
|
|
196
208
|
}
|
|
197
209
|
|
|
198
210
|
/** 初始化 - 评论操作按钮 */
|
|
199
|
-
private
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
211
|
+
private renderActions() {
|
|
212
|
+
this.renderActions_Vote()
|
|
213
|
+
this.renderActions_Reply()
|
|
214
|
+
|
|
215
|
+
// 管理员操作
|
|
216
|
+
this.renderActions_Collapse()
|
|
217
|
+
this.renderActions_Moderator()
|
|
218
|
+
this.renderActions_Pin()
|
|
219
|
+
this.renderActions_Edit()
|
|
220
|
+
this.renderActions_Del()
|
|
221
|
+
}
|
|
207
222
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
this.voteBtnDown.setClick(() => {
|
|
212
|
-
this.comment.getActions().vote('down')
|
|
213
|
-
})
|
|
214
|
-
}
|
|
215
|
-
}
|
|
223
|
+
// 操作按钮 - 投票
|
|
224
|
+
private renderActions_Vote() {
|
|
225
|
+
if (!this.ctx.conf.vote) return // 关闭投票功能
|
|
216
226
|
|
|
217
|
-
//
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
this
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
227
|
+
// 赞同按钮
|
|
228
|
+
this.voteBtnUp = new ActionBtn(this.ctx, () => `${this.ctx.$t('voteUp')} (${this.data.vote_up || 0})`).appendTo(this.$actions)
|
|
229
|
+
this.voteBtnUp.setClick(() => {
|
|
230
|
+
this.comment.getActions().vote('up')
|
|
231
|
+
})
|
|
232
|
+
|
|
233
|
+
// 反对按钮
|
|
234
|
+
if (this.ctx.conf.voteDown) {
|
|
235
|
+
this.voteBtnDown = new ActionBtn(this.ctx, () => `${this.ctx.$t('voteDown')} (${this.data.vote_down || 0})`).appendTo(this.$actions)
|
|
236
|
+
this.voteBtnDown.setClick(() => {
|
|
237
|
+
this.comment.getActions().vote('down')
|
|
228
238
|
})
|
|
229
239
|
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// 操作按钮 - 回复
|
|
243
|
+
private renderActions_Reply() {
|
|
244
|
+
if (!this.data.is_allow_reply) return // 不允许回复
|
|
230
245
|
|
|
231
|
-
|
|
246
|
+
const replyBtn = Utils.createElement(`<span>${this.ctx.$t('reply')}</span>`)
|
|
247
|
+
this.$actions.append(replyBtn)
|
|
248
|
+
replyBtn.addEventListener('click', (e) => {
|
|
249
|
+
e.stopPropagation() // 防止穿透
|
|
250
|
+
if (!this.cConf.onReplyBtnClick) {
|
|
251
|
+
this.ctx.replyComment(this.data, this.$el)
|
|
252
|
+
} else {
|
|
253
|
+
this.cConf.onReplyBtnClick()
|
|
254
|
+
}
|
|
255
|
+
})
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// 操作按钮 - 折叠
|
|
259
|
+
private renderActions_Collapse() {
|
|
232
260
|
const collapseBtn = new ActionBtn(this.ctx, {
|
|
233
261
|
text: () => (this.data.is_collapsed ? this.ctx.$t('expand') : this.ctx.$t('collapse')),
|
|
234
262
|
adminOnly: true
|
|
@@ -237,8 +265,10 @@ export default class CommentRender {
|
|
|
237
265
|
collapseBtn.setClick(() => {
|
|
238
266
|
this.comment.getActions().adminEdit('collapsed', collapseBtn)
|
|
239
267
|
})
|
|
268
|
+
}
|
|
240
269
|
|
|
241
|
-
|
|
270
|
+
// 操作按钮 - 审核
|
|
271
|
+
private renderActions_Moderator() {
|
|
242
272
|
const pendingBtn = new ActionBtn(this.ctx, {
|
|
243
273
|
text: () => (this.data.is_pending ? this.ctx.$t('pending') : this.ctx.$t('approved')),
|
|
244
274
|
adminOnly: true
|
|
@@ -247,8 +277,34 @@ export default class CommentRender {
|
|
|
247
277
|
pendingBtn.setClick(() => {
|
|
248
278
|
this.comment.getActions().adminEdit('pending', pendingBtn)
|
|
249
279
|
})
|
|
280
|
+
}
|
|
250
281
|
|
|
251
|
-
|
|
282
|
+
// 操作按钮 - 置顶
|
|
283
|
+
private renderActions_Pin() {
|
|
284
|
+
const pinnedBtn = new ActionBtn(this.ctx, {
|
|
285
|
+
text: () => (this.data.is_pinned ? this.ctx.$t('unpin') : this.ctx.$t('pin')),
|
|
286
|
+
adminOnly: true
|
|
287
|
+
})
|
|
288
|
+
pinnedBtn.appendTo(this.$actions)
|
|
289
|
+
pinnedBtn.setClick(() => {
|
|
290
|
+
this.comment.getActions().adminEdit('pinned', pinnedBtn)
|
|
291
|
+
})
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// 操作按钮 - 编辑
|
|
295
|
+
private renderActions_Edit() {
|
|
296
|
+
const editBtn = new ActionBtn(this.ctx, {
|
|
297
|
+
text: this.ctx.$t('edit'),
|
|
298
|
+
adminOnly: true
|
|
299
|
+
})
|
|
300
|
+
editBtn.appendTo(this.$actions)
|
|
301
|
+
editBtn.setClick(() => {
|
|
302
|
+
this.ctx.editComment(this.data, this.$el)
|
|
303
|
+
})
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// 操作按钮 - 删除
|
|
307
|
+
private renderActions_Del() {
|
|
252
308
|
const delBtn = new ActionBtn(this.ctx, {
|
|
253
309
|
text: this.ctx.$t('delete'),
|
|
254
310
|
confirm: true,
|
|
@@ -259,16 +315,6 @@ export default class CommentRender {
|
|
|
259
315
|
delBtn.setClick(() => {
|
|
260
316
|
this.comment.getActions().adminDelete(delBtn)
|
|
261
317
|
})
|
|
262
|
-
|
|
263
|
-
// 绑定置顶按钮事件
|
|
264
|
-
const pinnedBtn = new ActionBtn(this.ctx, {
|
|
265
|
-
text: () => (this.data.is_pinned ? this.ctx.$t('unpin') : this.ctx.$t('pin')),
|
|
266
|
-
adminOnly: true
|
|
267
|
-
})
|
|
268
|
-
pinnedBtn.appendTo(this.$actions)
|
|
269
|
-
pinnedBtn.setClick(() => {
|
|
270
|
-
this.comment.getActions().adminEdit('pinned', pendingBtn)
|
|
271
|
-
})
|
|
272
318
|
}
|
|
273
319
|
|
|
274
320
|
/** 内容限高检测 */
|
package/src/context.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { marked as libMarked } from 'marked'
|
|
|
2
2
|
import ArtalkConfig from '~/types/artalk-config'
|
|
3
3
|
import { CommentData, NotifyData } from '~/types/artalk-data'
|
|
4
4
|
import { Event } from '~/types/event'
|
|
5
|
-
import {
|
|
5
|
+
import getI18n, { I18n } from './i18n'
|
|
6
6
|
import User from './lib/user'
|
|
7
7
|
import ContextApi from '../types/context'
|
|
8
8
|
import Editor from './editor'
|
|
@@ -12,6 +12,7 @@ import SidebarLayer, { SidebarShowPayload } from './layer/sidebar-layer'
|
|
|
12
12
|
import CheckerLauncher, { CheckerCaptchaPayload, CheckerPayload } from './lib/checker'
|
|
13
13
|
import Layer, { GetLayerWrap } from './layer'
|
|
14
14
|
import Api from './api'
|
|
15
|
+
import List from './list'
|
|
15
16
|
|
|
16
17
|
/**
|
|
17
18
|
* Artalk Context
|
|
@@ -20,7 +21,7 @@ export default class Context implements ContextApi {
|
|
|
20
21
|
/* 持有同事类 */
|
|
21
22
|
private api!: Api
|
|
22
23
|
private editor!: Editor
|
|
23
|
-
private list
|
|
24
|
+
private list?: ListLite
|
|
24
25
|
private sidebarLayer!: SidebarLayer
|
|
25
26
|
private checkerLauncher!: CheckerLauncher
|
|
26
27
|
|
|
@@ -99,20 +100,29 @@ export default class Context implements ContextApi {
|
|
|
99
100
|
comment.getEl().remove()
|
|
100
101
|
this.commentList.splice(this.commentList.indexOf(comment), 1)
|
|
101
102
|
|
|
102
|
-
|
|
103
|
-
|
|
103
|
+
if (this.list) {
|
|
104
|
+
const listData = this.list.getData()
|
|
105
|
+
if (listData) listData.total -= 1 // 评论数减 1
|
|
104
106
|
|
|
105
|
-
|
|
107
|
+
this.list.refreshUI()
|
|
108
|
+
}
|
|
106
109
|
}
|
|
107
110
|
|
|
108
111
|
public clearAllComments() {
|
|
109
|
-
this.list
|
|
110
|
-
|
|
112
|
+
if (this.list) {
|
|
113
|
+
this.list.getCommentsWrapEl().innerHTML = ''
|
|
114
|
+
this.list.clearData()
|
|
115
|
+
}
|
|
116
|
+
|
|
111
117
|
this.commentList = []
|
|
112
118
|
}
|
|
113
119
|
|
|
114
120
|
public insertComment(commentData: CommentData) {
|
|
115
|
-
this.list
|
|
121
|
+
this.list?.insertComment(commentData)
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
public updateComment(commentData: CommentData): void {
|
|
125
|
+
this.list?.updateComment(commentData)
|
|
116
126
|
}
|
|
117
127
|
|
|
118
128
|
public replyComment(commentData: CommentData, $comment: HTMLElement, scroll?: boolean): void {
|
|
@@ -123,17 +133,37 @@ export default class Context implements ContextApi {
|
|
|
123
133
|
this.editor.cancelReply()
|
|
124
134
|
}
|
|
125
135
|
|
|
136
|
+
public editComment(commentData: CommentData, $comment: HTMLElement): void {
|
|
137
|
+
this.editor.setEditComment(commentData, $comment)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
public cancelEditComment(): void {
|
|
141
|
+
this.editor.cancelEditComment()
|
|
142
|
+
}
|
|
143
|
+
|
|
126
144
|
public updateNotifies(notifies: NotifyData[]): void {
|
|
127
|
-
this.list
|
|
145
|
+
this.list?.updateUnread(notifies)
|
|
128
146
|
}
|
|
129
147
|
|
|
130
148
|
/* 评论列表 */
|
|
131
149
|
public listReload(): void {
|
|
132
|
-
this.list
|
|
150
|
+
this.list?.reload()
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
public reload(): void {
|
|
154
|
+
this.listReload()
|
|
133
155
|
}
|
|
134
156
|
|
|
135
157
|
public listRefreshUI(): void {
|
|
136
|
-
this.list
|
|
158
|
+
this.list?.refreshUI()
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
public listHashGotoCheck(): void {
|
|
162
|
+
if (!this.list || this.list !instanceof List) return
|
|
163
|
+
const list = this.list as List
|
|
164
|
+
|
|
165
|
+
list.goToCommentDelay = false
|
|
166
|
+
list.checkGoToCommentByUrlHash()
|
|
137
167
|
}
|
|
138
168
|
|
|
139
169
|
/* 编辑器 */
|
|
@@ -224,14 +254,23 @@ export default class Context implements ContextApi {
|
|
|
224
254
|
|
|
225
255
|
/* i18n */
|
|
226
256
|
public $t(key: keyof I18n, args: {[key: string]: string} = {}): string {
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
257
|
+
return getI18n(this.conf.locale, key, args)
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
public setDarkMode(darkMode: boolean): void {
|
|
261
|
+
const darkModeClassName = 'atk-dark-mode'
|
|
262
|
+
|
|
263
|
+
this.conf.darkMode = darkMode
|
|
264
|
+
this.trigger('conf-updated')
|
|
231
265
|
|
|
232
|
-
|
|
233
|
-
|
|
266
|
+
if (this.conf.darkMode) this.$root.classList.add(darkModeClassName)
|
|
267
|
+
else this.$root.classList.remove(darkModeClassName)
|
|
234
268
|
|
|
235
|
-
|
|
269
|
+
// for Layer
|
|
270
|
+
const { $wrap: $layerWrap } = GetLayerWrap(this)
|
|
271
|
+
if ($layerWrap) {
|
|
272
|
+
if (this.conf.darkMode) $layerWrap.classList.add(darkModeClassName)
|
|
273
|
+
else $layerWrap.classList.remove(darkModeClassName)
|
|
274
|
+
}
|
|
236
275
|
}
|
|
237
276
|
}
|
package/src/editor/editor.ts
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import '@/style/editor.less'
|
|
2
|
-
|
|
3
1
|
import { CommentData } from '~/types/artalk-data'
|
|
4
2
|
import Context from '~/types/context'
|
|
5
3
|
import Component from '../lib/component'
|
|
@@ -31,10 +29,18 @@ export default class Editor extends Component {
|
|
|
31
29
|
public $submitBtn: HTMLButtonElement
|
|
32
30
|
public $notifyWrap: HTMLElement
|
|
33
31
|
|
|
32
|
+
// Editor 的不同状态
|
|
33
|
+
private isTraveling = false
|
|
34
|
+
|
|
35
|
+
/** 回复评论 */
|
|
34
36
|
private replyComment: CommentData|null = null
|
|
35
37
|
private $sendReply: HTMLElement|null = null
|
|
38
|
+
public get isReplyMode() { return this.replyComment !== null }
|
|
36
39
|
|
|
37
|
-
|
|
40
|
+
/** 编辑评论 */
|
|
41
|
+
private editComment: CommentData|null = null
|
|
42
|
+
private $editCancelBtn: HTMLElement|null = null
|
|
43
|
+
public get isEditMode() { return this.editComment !== null }
|
|
38
44
|
|
|
39
45
|
/** 启用的插件 */
|
|
40
46
|
private readonly ENABLED_PLUGS = [ EmoticonsPlug, UploadPlug, PreviewPlug, HeaderInputPlug ]
|
|
@@ -98,6 +104,8 @@ export default class Editor extends Component {
|
|
|
98
104
|
}
|
|
99
105
|
|
|
100
106
|
private onHeaderInput(key: string, $input: HTMLInputElement) {
|
|
107
|
+
if (this.isEditMode) return // 评论编辑模式,不修改个人信息
|
|
108
|
+
|
|
101
109
|
this.user.update({
|
|
102
110
|
[key]: $input.value.trim()
|
|
103
111
|
})
|
|
@@ -128,8 +136,13 @@ export default class Editor extends Component {
|
|
|
128
136
|
})
|
|
129
137
|
}
|
|
130
138
|
|
|
139
|
+
private refreshSendBtnText() {
|
|
140
|
+
if (this.isEditMode) this.$submitBtn.innerText = this.$t('save')
|
|
141
|
+
else this.$submitBtn.innerText = this.ctx.conf.sendBtn || this.$t('send')
|
|
142
|
+
}
|
|
143
|
+
|
|
131
144
|
private initSubmitBtn() {
|
|
132
|
-
this
|
|
145
|
+
this.refreshSendBtnText()
|
|
133
146
|
this.$submitBtn.addEventListener('click', () => (this.submit()))
|
|
134
147
|
}
|
|
135
148
|
|
|
@@ -186,21 +199,16 @@ export default class Editor extends Component {
|
|
|
186
199
|
}
|
|
187
200
|
}
|
|
188
201
|
|
|
189
|
-
public clearEditor() {
|
|
190
|
-
this.setContent('')
|
|
191
|
-
this.cancelReply()
|
|
192
|
-
}
|
|
193
|
-
|
|
194
202
|
private adjustTextareaHeight() {
|
|
195
203
|
const diff = this.$textarea.offsetHeight - this.$textarea.clientHeight
|
|
196
204
|
this.$textarea.style.height = '0px' // it's a magic. 若不加此行,内容减少,高度回不去
|
|
197
205
|
this.$textarea.style.height = `${this.$textarea.scrollHeight + diff}px`
|
|
198
206
|
}
|
|
199
207
|
|
|
208
|
+
/** 设置回复评论 */
|
|
200
209
|
public setReply(commentData: CommentData, $comment: HTMLElement, scroll = true) {
|
|
201
|
-
if (this.
|
|
202
|
-
|
|
203
|
-
}
|
|
210
|
+
if (this.isEditMode) this.cancelEditComment()
|
|
211
|
+
if (this.isReplyMode) this.cancelReply()
|
|
204
212
|
|
|
205
213
|
if (this.$sendReply === null) {
|
|
206
214
|
this.$sendReply = Utils.createElement(`<div class="atk-send-reply">${this.$t('reply')} <span class="atk-text"></span><span class="atk-cancel">×</span></div>`);
|
|
@@ -221,6 +229,7 @@ export default class Editor extends Component {
|
|
|
221
229
|
this.$textarea.focus()
|
|
222
230
|
}
|
|
223
231
|
|
|
232
|
+
/** 取消回复评论 */
|
|
224
233
|
public cancelReply() {
|
|
225
234
|
if (this.$sendReply !== null) {
|
|
226
235
|
this.$sendReply.remove()
|
|
@@ -233,6 +242,53 @@ export default class Editor extends Component {
|
|
|
233
242
|
}
|
|
234
243
|
}
|
|
235
244
|
|
|
245
|
+
/** 设置编辑评论 */
|
|
246
|
+
public setEditComment(commentData: CommentData, $comment: HTMLElement) {
|
|
247
|
+
if (this.isEditMode) this.cancelEditComment()
|
|
248
|
+
if (this.isReplyMode) this.cancelReply()
|
|
249
|
+
|
|
250
|
+
if (this.$editCancelBtn === null) {
|
|
251
|
+
this.$editCancelBtn = Utils.createElement(`<div class="atk-send-reply">${this.$t('editCancel')}<span class="atk-cancel">×</span></div>`);
|
|
252
|
+
this.$editCancelBtn.onclick = () => {
|
|
253
|
+
this.cancelEditComment()
|
|
254
|
+
}
|
|
255
|
+
this.$textareaWrap.append(this.$editCancelBtn)
|
|
256
|
+
}
|
|
257
|
+
this.editComment = commentData
|
|
258
|
+
|
|
259
|
+
this.$header.style.display = 'none' // TODO 暂时隐藏
|
|
260
|
+
|
|
261
|
+
this.travel($comment)
|
|
262
|
+
|
|
263
|
+
this.$nick.value = commentData.nick
|
|
264
|
+
this.$email.value = commentData.email || ''
|
|
265
|
+
this.$link.value = commentData.link
|
|
266
|
+
|
|
267
|
+
this.setContent(commentData.content)
|
|
268
|
+
this.$textarea.focus()
|
|
269
|
+
|
|
270
|
+
this.refreshSendBtnText()
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/** 取消编辑评论 */
|
|
274
|
+
public cancelEditComment() {
|
|
275
|
+
if (this.$editCancelBtn !== null) {
|
|
276
|
+
this.$editCancelBtn.remove()
|
|
277
|
+
this.$editCancelBtn = null
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
this.editComment = null
|
|
281
|
+
this.travelBack()
|
|
282
|
+
|
|
283
|
+
this.$nick.value = this.user.data.nick
|
|
284
|
+
this.$email.value = this.user.data.email
|
|
285
|
+
this.$link.value = this.user.data.link
|
|
286
|
+
|
|
287
|
+
this.setContent('')
|
|
288
|
+
this.refreshSendBtnText()
|
|
289
|
+
this.$header.style.display = '' // TODO
|
|
290
|
+
}
|
|
291
|
+
|
|
236
292
|
public showNotify(msg: string, type: "i"|"s"|"w"|"e") {
|
|
237
293
|
Ui.showNotify(this.$notifyWrap, msg, type)
|
|
238
294
|
}
|
|
@@ -245,7 +301,7 @@ export default class Editor extends Component {
|
|
|
245
301
|
Ui.hideLoading(this.$el)
|
|
246
302
|
}
|
|
247
303
|
|
|
248
|
-
/**
|
|
304
|
+
/** 点击评论提交按钮事件 */
|
|
249
305
|
public async submit() {
|
|
250
306
|
if (this.getFinalContent().trim() === '') {
|
|
251
307
|
this.$textarea.focus()
|
|
@@ -256,8 +312,19 @@ export default class Editor extends Component {
|
|
|
256
312
|
|
|
257
313
|
this.showLoading()
|
|
258
314
|
|
|
315
|
+
if (!this.isEditMode) {
|
|
316
|
+
await this.submitAdd()
|
|
317
|
+
} else {
|
|
318
|
+
await this.submitEdit()
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
this.hideLoading()
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/** 创建评论 */
|
|
325
|
+
public async submitAdd() {
|
|
259
326
|
try {
|
|
260
|
-
const nComment = await this.ctx.getApi().add({
|
|
327
|
+
const nComment = await this.ctx.getApi().comment.add({
|
|
261
328
|
content: this.getFinalContent(),
|
|
262
329
|
nick: this.user.data.nick,
|
|
263
330
|
email: this.user.data.email,
|
|
@@ -274,14 +341,40 @@ export default class Editor extends Component {
|
|
|
274
341
|
}
|
|
275
342
|
|
|
276
343
|
this.ctx.insertComment(nComment)
|
|
277
|
-
|
|
344
|
+
|
|
345
|
+
// 复原编辑器
|
|
346
|
+
this.setContent('')
|
|
347
|
+
this.cancelReply()
|
|
348
|
+
|
|
349
|
+
this.ctx.trigger('editor-submitted')
|
|
350
|
+
} catch (err: any) {
|
|
351
|
+
console.error(err)
|
|
352
|
+
this.showNotify(`${this.$t('commentFail')},${err.msg || String(err)}`, 'e')
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/** 修改评论 */
|
|
357
|
+
public async submitEdit() {
|
|
358
|
+
try {
|
|
359
|
+
const saveData = {
|
|
360
|
+
content: this.getFinalContent(),
|
|
361
|
+
nick: this.$nick.value,
|
|
362
|
+
email: this.$email.value,
|
|
363
|
+
link: this.$link.value,
|
|
364
|
+
}
|
|
365
|
+
const nComment = await this.ctx.getApi().comment.commentEdit({
|
|
366
|
+
...this.editComment, ...saveData
|
|
367
|
+
})
|
|
368
|
+
this.ctx.updateComment(nComment)
|
|
369
|
+
|
|
370
|
+
// 复原编辑器
|
|
371
|
+
this.setContent('')
|
|
372
|
+
this.cancelEditComment()
|
|
373
|
+
|
|
278
374
|
this.ctx.trigger('editor-submitted')
|
|
279
375
|
} catch (err: any) {
|
|
280
376
|
console.error(err)
|
|
281
377
|
this.showNotify(`${this.$t('commentFail')},${err.msg || String(err)}`, 'e')
|
|
282
|
-
return
|
|
283
|
-
} finally {
|
|
284
|
-
this.hideLoading()
|
|
285
378
|
}
|
|
286
379
|
}
|
|
287
380
|
|
|
@@ -39,7 +39,7 @@ export default class HeaderInputPlug extends EditorPlug {
|
|
|
39
39
|
this.queryUserInfo.timeout = window.setTimeout(() => {
|
|
40
40
|
this.queryUserInfo.timeout = null // 清理
|
|
41
41
|
|
|
42
|
-
const {req, abort} = this.ctx.getApi().userGet(
|
|
42
|
+
const {req, abort} = this.ctx.getApi().user.userGet(
|
|
43
43
|
this.ctx.user.data.nick, this.ctx.user.data.email
|
|
44
44
|
)
|
|
45
45
|
this.queryUserInfo.abortFunc = abort
|
|
@@ -99,7 +99,7 @@ export default class UploadPlug extends EditorPlug {
|
|
|
99
99
|
try {
|
|
100
100
|
if (!this.ctx.conf.imgUploader) {
|
|
101
101
|
// 使用 ArtalkGo 进行图片上传
|
|
102
|
-
resp = await this.ctx.getApi().imgUpload(file)
|
|
102
|
+
resp = await this.ctx.getApi().upload.imgUpload(file)
|
|
103
103
|
} else {
|
|
104
104
|
// 使用自定义的图片上传器
|
|
105
105
|
resp = {img_url: await this.ctx.conf.imgUploader(file)}
|
package/src/i18n/en.ts
CHANGED
|
@@ -3,6 +3,7 @@ export default {
|
|
|
3
3
|
placeholder: 'Leave a comment',
|
|
4
4
|
noComment: 'No Comment',
|
|
5
5
|
send: 'Send',
|
|
6
|
+
save: 'Save',
|
|
6
7
|
nick: 'Nickname',
|
|
7
8
|
email: 'Email',
|
|
8
9
|
link: 'Website',
|
|
@@ -42,6 +43,8 @@ export default {
|
|
|
42
43
|
approved: 'Approved',
|
|
43
44
|
pending: 'Pending',
|
|
44
45
|
pendingMsg: 'Pending, visible only to commenter.',
|
|
46
|
+
edit: 'Edit',
|
|
47
|
+
editCancel: 'Cancel Edit',
|
|
45
48
|
delete: 'Delete',
|
|
46
49
|
deleteConfirm: 'Confirm',
|
|
47
50
|
pin: 'Pin',
|