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
package/src/lib/api.ts DELETED
@@ -1,380 +0,0 @@
1
- import { CommentData, ListData, UserData, PageData, SiteData, NotifyData } from '~/types/artalk-data'
2
- import Context from '../Context'
3
-
4
- export default class Api {
5
- private ctx: Context
6
- private serverURL: string
7
-
8
- constructor (ctx: Context) {
9
- this.ctx = ctx
10
- this.serverURL = ctx.conf.server
11
- }
12
-
13
- public get(offset: number, type?: string, paramsEditor?: (params: any) => void): Promise<ListData> {
14
- const params: any = {
15
- page_key: this.ctx.conf.pageKey,
16
- limit: this.ctx.conf.readMore?.pageSize || 15,
17
- offset,
18
- }
19
-
20
- if (type) {
21
- params.type = type
22
- }
23
-
24
- if (this.ctx.user.checkHasBasicUserInfo()) {
25
- params.name = this.ctx.user.data.nick
26
- params.email = this.ctx.user.data.email
27
- }
28
-
29
- if (this.ctx.conf.site) params.site_name = this.ctx.conf.site
30
-
31
- if (paramsEditor) paramsEditor(params)
32
-
33
- return CommonFetch(this.ctx, `${this.serverURL}/get`, {
34
- method: 'POST',
35
- body: getFormData(params),
36
- }).then((json) => (json.data as ListData))
37
- }
38
-
39
- public add(comment: { nick: string, email: string, link: string, content: string, rid: number }): Promise<CommentData> {
40
- const params: any = {
41
- name: comment.nick,
42
- email: comment.email,
43
- link: comment.link,
44
- content: comment.content,
45
- rid: comment.rid,
46
- page_key: this.ctx.conf.pageKey,
47
- page_url: this.ctx.conf.pageUrl || '',
48
- page_title: this.ctx.conf.pageTitle || '',
49
- }
50
-
51
- if (this.ctx.conf.site) params.site_name = this.ctx.conf.site
52
-
53
- return CommonFetch(this.ctx, `${this.serverURL}/add`, {
54
- method: 'POST',
55
- body: getFormData(params),
56
- }).then((json) => (json.data.comment as CommentData))
57
- }
58
-
59
- public login(name: string, email: string, password: string): Promise<string> {
60
- const params: any = {
61
- name, email, password
62
- }
63
-
64
- if (this.ctx.conf.site) params.site_name = this.ctx.conf.site
65
-
66
- return CommonFetch(this.ctx, `${this.serverURL}/login`, {
67
- method: 'POST',
68
- body: getFormData(params),
69
- }).then((json) => (json.data.token))
70
- }
71
-
72
- public userGet(name: string, email: string) {
73
- const ctrl = new AbortController()
74
- const { signal } = ctrl
75
-
76
- const params: any = {
77
- name, email
78
- }
79
-
80
- if (this.ctx.conf.site) params.site_name = this.ctx.conf.site
81
-
82
- const req = CommonFetch(this.ctx, `${this.serverURL}/user-get`, {
83
- method: 'POST',
84
- body: getFormData(params),
85
- signal,
86
- }).then((json) => ({
87
- user: json.data.user as UserData|null,
88
- is_login: json.data.is_login as boolean,
89
- unread: (json.data.unread || []) as NotifyData[],
90
- unread_count: json.data.unread_count || 0,
91
- }))
92
-
93
- return {
94
- req,
95
- abort: () => { ctrl.abort() },
96
- }
97
- }
98
-
99
- public vote(targetID: number, type: string) {
100
- const params: any = {
101
- target_id: targetID,
102
- type,
103
- }
104
-
105
- if (this.ctx.user.checkHasBasicUserInfo()) {
106
- params.name = this.ctx.user.data.nick
107
- params.email = this.ctx.user.data.email
108
- }
109
- if (this.ctx.conf.site) params.site_name = this.ctx.conf.site
110
-
111
- return CommonFetch(this.ctx, `${this.serverURL}/vote`, {
112
- method: 'POST',
113
- body: getFormData(params),
114
- }).then((json) => ((json.data?.vote_num || 0) as number))
115
- }
116
-
117
- public markRead(notifyKey: string, readAll = false) {
118
- const params: any = {
119
- notify_key: notifyKey,
120
- }
121
-
122
- if (readAll) {
123
- delete params.notify_key
124
- params.read_all = true
125
- params.name = this.ctx.user.data.nick
126
- params.email = this.ctx.user.data.email
127
- }
128
-
129
- if (this.ctx.conf.site) params.site_name = this.ctx.conf.site
130
-
131
- return CommonFetch(this.ctx, `${this.serverURL}/mark-read`, {
132
- method: 'POST',
133
- body: getFormData(params),
134
- }).then((json) => (json.success as boolean))
135
- }
136
-
137
- public captchaGet(): Promise<string> {
138
- return CommonFetch(this.ctx, `${this.serverURL}/captcha/refresh`, {
139
- method: 'GET',
140
- }).then((json) => {
141
- if (!!json.success && !!json.data.img_data) {
142
- return json.data.img_data
143
- }
144
-
145
- return ''
146
- })
147
- }
148
-
149
- public captchaCheck(value: string): Promise<string> {
150
- return CommonFetch(this.ctx, `${this.serverURL}/captcha/check?${new URLSearchParams({ value })}`, {
151
- method: 'GET',
152
- }).then((json) => {
153
- if (!json.success && !!json.data.img_data) {
154
- return json.data.img_data
155
- }
156
-
157
- return ''
158
- })
159
- }
160
-
161
- public commentEdit(data: CommentData) {
162
- const params: any = {
163
- ...data,
164
- }
165
-
166
- if (params.is_collapsed !== undefined) params.is_collapsed = params.is_collapsed ? '1' : '0'
167
- if (params.is_pending !== undefined) params.is_pending = params.is_pending ? '1' : '0'
168
-
169
- if (this.ctx.conf.site) params.site_name = this.ctx.conf.site
170
-
171
- return CommonFetch(this.ctx, `${this.serverURL}/admin/comment-edit`, {
172
- method: 'POST',
173
- body: getFormData(params),
174
- }).then((json) => (json.data.comment as CommentData))
175
- }
176
-
177
- public pageEdit(data: PageData) {
178
- const params: any = {
179
- id: data.id,
180
- key: data.key,
181
- title: data.title,
182
- admin_only: data.admin_only ? '1' : '0',
183
- }
184
-
185
- if (this.ctx.conf.site) params.site_name = this.ctx.conf.site
186
-
187
- return CommonFetch(this.ctx, `${this.serverURL}/admin/page-edit`, {
188
- method: 'POST',
189
- body: getFormData(params),
190
- }).then((json) => (json.data.page as PageData))
191
- }
192
-
193
- public pageFetch(id: number) {
194
- const params: any = {
195
- id,
196
- }
197
-
198
- return CommonFetch(this.ctx, `${this.serverURL}/admin/page-fetch`, {
199
- method: 'POST',
200
- body: getFormData(params),
201
- }).then((json) => (json.data.page as PageData))
202
- }
203
-
204
- public siteGet(): Promise<SiteData[]> {
205
- const params: any = {}
206
-
207
- return CommonFetch(this.ctx, `${this.serverURL}/admin/site-get`, {
208
- method: 'POST',
209
- body: getFormData(params),
210
- }).then((json) => (json.data.sites as SiteData[]))
211
- }
212
-
213
- public pageGet(siteName?: string): Promise<PageData[]> {
214
- const params: any = {
215
- site_name: siteName || ''
216
- }
217
-
218
- return CommonFetch(this.ctx, `${this.serverURL}/admin/page-get`, {
219
- method: 'POST',
220
- body: getFormData(params),
221
- }).then((json) => (json.data.pages as PageData[]))
222
- }
223
-
224
- public pageDel(pageKey: string, siteName?: string) {
225
- const params: any = {
226
- key: String(pageKey),
227
- site_name: siteName || '',
228
- }
229
-
230
- return CommonFetch(this.ctx, `${this.serverURL}/admin/page-del`, {
231
- method: 'POST',
232
- body: getFormData(params),
233
- }).then((json) => (json.success as boolean))
234
- }
235
-
236
- public siteDel(id: number, delContent = false) {
237
- const params: any = {
238
- id,
239
- del_content: delContent ? '1' : '0'
240
- }
241
-
242
- return CommonFetch(this.ctx, `${this.serverURL}/admin/site-del`, {
243
- method: 'POST',
244
- body: getFormData(params),
245
- }).then((json) => (json.success as boolean))
246
- }
247
-
248
- public siteAdd(name: string, urls: string) {
249
- const params: any = {
250
- name, urls,
251
- }
252
-
253
- return CommonFetch(this.ctx, `${this.serverURL}/admin/site-add`, {
254
- method: 'POST',
255
- body: getFormData(params),
256
- }).then((json) => (json.site as SiteData))
257
- }
258
-
259
- public siteEdit(id: number, data: {
260
- name: string
261
- urls: string
262
- }) {
263
- const params: any = {
264
- id,
265
- name: data.name,
266
- urls: data.urls,
267
- }
268
-
269
- return CommonFetch(this.ctx, `${this.serverURL}/admin/site-edit`, {
270
- method: 'POST',
271
- body: getFormData(params),
272
- }).then((json) => (json.site as SiteData))
273
- }
274
-
275
- public commentDel(commentID: number, siteName?: string) {
276
- const params: any = {
277
- id: String(commentID),
278
- site_name: siteName || '',
279
- }
280
-
281
- return CommonFetch(this.ctx, `${this.serverURL}/admin/comment-del`, {
282
- method: 'POST',
283
- body: getFormData(params),
284
- }).then((json) => (json.success as boolean))
285
- }
286
- }
287
-
288
- /** 公共请求函数 */
289
- function CommonFetch(ctx: Context, input: RequestInfo, init: RequestInit): Promise<any> {
290
- if (ctx.user.data.token) {
291
- const requestHeaders: HeadersInit = new Headers();
292
- requestHeaders.set('Authorization', `Bearer ${ctx.user.data.token}`);
293
-
294
- init.headers = requestHeaders
295
- }
296
-
297
- // 15s timeout
298
- return timeoutPromise(15000, fetch(input, init)).then(async (resp) => {
299
- if (!resp.ok && resp.status !== 401) {
300
- throw new Error(`请求响应 ${resp.status}`)
301
- }
302
-
303
- // 解析获取响应的 json
304
- let json: any = await resp.json()
305
-
306
- // 重新发起请求
307
- const recall = (resolve, reject) => {
308
- CommonFetch(ctx, input, init).then(d => {
309
- resolve(d)
310
- }).catch(err => {
311
- reject(err)
312
- })
313
- }
314
-
315
- if (json.data && json.data.need_captcha) {
316
- // 请求需要验证码
317
- json = await (new Promise<any>((resolve, reject) => {
318
- ctx.dispatchEvent('checker-captcha', {
319
- imgData: json.data.img_data,
320
- onSuccess: () => {
321
- recall(resolve, reject)
322
- },
323
- onCancel: () => {
324
- reject(json)
325
- }
326
- })
327
- }))
328
- } else if ((json.data && json.data.need_login) || resp.status === 401) {
329
- // 请求需要管理员权限
330
- json = await (new Promise<any>((resolve, reject) => {
331
- ctx.dispatchEvent('checker-admin', {
332
- onSuccess: () => {
333
- recall(resolve, reject)
334
- },
335
- onCancel: () => {
336
- reject(json)
337
- }
338
- })
339
- }))
340
- }
341
-
342
- if (!json.success)
343
- throw json // throw 相当于 reject(json)
344
- else
345
- return json
346
- })
347
- .catch(err => {
348
- console.error(err)
349
-
350
- if (err instanceof TypeError)
351
- throw new Error(`网络错误`)
352
-
353
- throw err
354
- })
355
- }
356
-
357
- function getFormData (object: any): FormData {
358
- const formData = new FormData()
359
- Object.keys(object).forEach(key => formData.append(key, String(object[key])))
360
- return formData
361
- }
362
-
363
- /** TODO: 我靠,一个 timeout,都要丑陋的实现 */
364
- function timeoutPromise<T>(ms: number, promise: Promise<T>): Promise<T> {
365
- return new Promise((resolve, reject) => {
366
- const timeoutId = setTimeout(() => {
367
- reject(new Error("请求超时"))
368
- }, ms);
369
- promise.then(
370
- (res) => {
371
- clearTimeout(timeoutId);
372
- resolve(res);
373
- },
374
- (err) => {
375
- clearTimeout(timeoutId);
376
- reject(err);
377
- }
378
- );
379
- })
380
- }
@@ -1,164 +0,0 @@
1
- import Context from "../Context"
2
- import * as Utils from "./utils"
3
- import User from "./user"
4
- import * as Ui from './ui'
5
- import BuildLayer from "../components/Layer"
6
- import Api from "./api"
7
- import { CheckerCaptchaConf } from '../Event'
8
-
9
- interface CheckerConf {
10
- el?: HTMLElement
11
- body: (that: Checker) => HTMLElement
12
- request: (that: Checker, inputVal: string) => Promise<string>
13
- onSuccess?: (that: Checker, data: string, inputVal: string, formEl: HTMLElement) => void
14
- onError?: (that: Checker, err: any, inputVal: string, formEl: HTMLElement) => void
15
- }
16
-
17
- const AdminChecker = {
18
- request: (that, inputVal) => {
19
- const data = {
20
- name: that.user.data.nick,
21
- email: that.user.data.email,
22
- password: inputVal
23
- }
24
-
25
- return new Api(that.ctx).login(data.name, data.email, data.password)
26
- },
27
- body: () => Utils.createElement('<span>敲入密码来验证管理员身份:</span>'),
28
- onSuccess: (that, userToken, inputVal, formEl) => {
29
- that.user.data.isAdmin = true
30
- that.user.data.token = userToken
31
- that.user.save()
32
- that.ctx.dispatchEvent('user-changed')
33
- that.ctx.dispatchEvent('list-reload')
34
- },
35
- onError: (that, err, inputVal, formEl) => {
36
-
37
- }
38
-
39
- } as CheckerConf
40
-
41
- const CaptchaChecker = {
42
- request: (that, inputVal) => (new Api(that.ctx).captchaCheck(inputVal)),
43
- body: (that) => {
44
- const elem = Utils.createElement(`<span><img class="atk-captcha-img" src="${that.submitCaptchaImgData || ''}" alt="验证码">敲入验证码继续:</span>`);
45
-
46
- // 刷新验证码
47
- elem.querySelector<HTMLElement>('.atk-captcha-img')!.onclick = () => {
48
- const imgEl = elem.querySelector('.atk-captcha-img')
49
- new Api(that.ctx).captchaGet()
50
- .then((imgData) => {
51
- imgEl!.setAttribute('src', imgData)
52
- })
53
- .catch((err) => {
54
- console.error('验证码获取失败 ', err)
55
- })
56
- }
57
- return elem
58
- },
59
- onSuccess: (that, msg, data, inputVal, formEl) => {
60
- that.submitCaptchaVal = inputVal
61
- },
62
- onError: (that, err, inputVal, formEl) => {
63
- formEl.querySelector<HTMLElement>('.atk-captcha-img')!.click() // 刷新验证码
64
- }
65
- } as CheckerConf
66
-
67
- export default class Checker {
68
- public ctx: Context
69
- public user: User
70
-
71
- public submitCaptchaVal?: string
72
- public submitCaptchaImgData?: string
73
-
74
- constructor (ctx: Context) {
75
- this.ctx = ctx
76
- this.user = ctx.user
77
-
78
- this.ctx.addEventListener('checker-captcha', (conf) => {
79
- if (conf.imgData) {
80
- this.submitCaptchaImgData = conf.imgData
81
- }
82
- this.check('captcha', conf)
83
- })
84
-
85
- this.ctx.addEventListener('checker-admin', (conf) => {
86
- this.check('admin', conf)
87
- })
88
- }
89
-
90
- public check (name: 'admin'|'captcha', conf: CheckerCaptchaConf) {
91
- const checker = {
92
- admin: AdminChecker,
93
- captcha: CaptchaChecker
94
- }[name]
95
-
96
- const layer = BuildLayer(this.ctx, `checker-${new Date().getTime()}`)
97
- layer.setMaskClickHide(false)
98
- layer.show()
99
-
100
- const formEl = Utils.createElement()
101
- formEl.appendChild(checker.body(this))
102
-
103
- const input = Utils.createElement<HTMLInputElement>(`<input id="check" type="${(name === 'admin' ? 'password' : 'text')}" required placeholder="">`)
104
- formEl.appendChild(input)
105
- setTimeout(() => {
106
- input.focus() // 延迟以保证有效
107
- }, 80)
108
-
109
- input.onkeyup = (evt) => {
110
- if (evt.key === 'Enter' || evt.keyCode === 13) { // 按下回车键
111
- evt.preventDefault();
112
- (layer.getEl().querySelector<HTMLButtonElement>('button[data-action="confirm"]'))!.click()
113
- }
114
- }
115
-
116
- let btnRawText: string|undefined
117
- const dialogEl = Ui.buildDialog(formEl, (btnElem: HTMLElement) => {
118
- const inputVal = input.value.trim()
119
- if (!btnRawText) {
120
- btnRawText = btnElem.innerText
121
- }
122
- const btnTextSet = (btnText: string) => {
123
- btnElem.innerText = btnText
124
- btnElem.classList.add('error')
125
- }
126
- const btnTextRestore = () => {
127
- btnElem.innerText = btnRawText || ''
128
- btnElem.classList.remove('error')
129
- }
130
-
131
- btnElem.innerText = '加载中...'
132
- checker.request(this, inputVal)
133
- .then(data => {
134
- // 请求成功
135
- layer.disposeNow()
136
- if (checker.onSuccess) checker.onSuccess(this, data, inputVal, formEl)
137
- if (conf.onSuccess) conf.onSuccess(inputVal, dialogEl)
138
- })
139
- .catch(err => {
140
- btnTextSet(String(err.msg || String(err)))
141
- if (checker.onError)
142
- checker.onError(this, err, inputVal, formEl)
143
-
144
- const tf = setTimeout(() => {
145
- btnTextRestore()
146
- }, 3000)
147
- input.onfocus = () => {
148
- btnTextRestore()
149
- clearTimeout(tf)
150
- }
151
- })
152
-
153
- return false
154
- }, () => {
155
- layer.disposeNow()
156
- if (conf.onCancel) conf.onCancel()
157
- return false
158
- })
159
-
160
- layer.getEl().append(dialogEl)
161
-
162
- if (conf.onMount) conf.onMount(dialogEl) // onMount
163
- }
164
- }