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.
Files changed (105) hide show
  1. package/dist/Artalk.css +1 -1
  2. package/dist/Artalk.es.js +3184 -2888
  3. package/dist/Artalk.iife.js +1 -1
  4. package/dist/Artalk.js +1 -1
  5. package/dist/ArtalkLite.css +1 -1
  6. package/dist/ArtalkLite.es.js +3157 -2887
  7. package/dist/ArtalkLite.iife.js +1 -1
  8. package/dist/ArtalkLite.js +1 -1
  9. package/dist/src/api/admin.d.ts +10 -0
  10. package/dist/src/api/api-base.d.ts +14 -0
  11. package/dist/src/api/captcha.d.ts +14 -0
  12. package/dist/src/api/comment.d.ts +29 -0
  13. package/dist/src/api/index.d.ts +31 -0
  14. package/dist/src/api/page.d.ts +22 -0
  15. package/dist/src/api/request.d.ts +12 -0
  16. package/dist/src/api/site.d.ts +17 -0
  17. package/dist/src/api/system.d.ts +13 -0
  18. package/dist/src/api/upload.d.ts +11 -0
  19. package/dist/src/api/user.d.ts +28 -0
  20. package/dist/src/artalk.d.ts +56 -0
  21. package/dist/src/comment/actions.d.ts +17 -0
  22. package/dist/src/comment/comment.d.ts +67 -0
  23. package/dist/src/comment/index.d.ts +2 -0
  24. package/dist/src/comment/render.d.ts +76 -0
  25. package/dist/src/components/action-btn.d.ts +52 -0
  26. package/dist/src/components/dialog.d.ts +18 -0
  27. package/dist/src/components/pagination.d.ts +30 -0
  28. package/dist/src/components/read-more-btn.d.ts +33 -0
  29. package/dist/src/context.d.ts +72 -0
  30. package/dist/src/defaults.d.ts +3 -0
  31. package/dist/src/editor/editor.d.ts +84 -0
  32. package/dist/src/editor/index.d.ts +2 -0
  33. package/dist/src/editor/plugs/editor-plug.d.ts +26 -0
  34. package/dist/src/editor/plugs/emoticons-plug.d.ts +36 -0
  35. package/dist/src/editor/plugs/header-input-plug.d.ts +13 -0
  36. package/dist/src/editor/plugs/preview-plug.d.ts +10 -0
  37. package/dist/src/editor/plugs/upload-plug.d.ts +10 -0
  38. package/dist/src/i18n/en.d.ts +69 -0
  39. package/dist/src/i18n/index.d.ts +294 -0
  40. package/dist/src/i18n/zh-CN.d.ts +3 -0
  41. package/dist/src/layer/index.d.ts +3 -0
  42. package/dist/src/layer/layer.d.ts +32 -0
  43. package/dist/src/layer/sidebar-layer.d.ts +20 -0
  44. package/dist/src/lib/checker/admin-checker.d.ts +3 -0
  45. package/dist/src/lib/checker/captcha-checker.d.ts +3 -0
  46. package/dist/src/lib/checker/index.d.ts +42 -0
  47. package/dist/src/lib/component.d.ts +12 -0
  48. package/dist/src/lib/detect.d.ts +10 -0
  49. package/dist/src/lib/stat.d.ts +15 -0
  50. package/dist/src/lib/ui.d.ts +23 -0
  51. package/dist/src/lib/user.d.ts +13 -0
  52. package/dist/src/lib/utils.d.ts +39 -0
  53. package/dist/src/list/index.d.ts +2 -0
  54. package/dist/src/list/list-lite.d.ts +63 -0
  55. package/dist/src/list/list-nest.d.ts +10 -0
  56. package/dist/src/list/list.d.ts +26 -0
  57. package/dist/src/main.d.ts +4 -0
  58. package/dist/types/artalk-config.d.ts +132 -0
  59. package/dist/types/artalk-data.d.ts +204 -0
  60. package/dist/types/context.d.ts +146 -0
  61. package/dist/types/event.d.ts +28 -0
  62. package/dist/types/global.d.ts +0 -0
  63. package/dist/types/plug.d.ts +4 -0
  64. package/index.html +3 -3
  65. package/package.json +5 -5
  66. package/src/api/admin.ts +18 -0
  67. package/src/api/api-base.ts +27 -0
  68. package/src/api/captcha.ts +24 -0
  69. package/src/api/comment.ts +84 -0
  70. package/src/api/index.ts +25 -361
  71. package/src/api/page.ts +85 -0
  72. package/src/api/request.ts +12 -8
  73. package/src/api/site.ts +51 -0
  74. package/src/api/system.ts +39 -0
  75. package/src/api/upload.ts +29 -0
  76. package/src/api/user.ts +75 -0
  77. package/src/artalk.ts +75 -76
  78. package/src/comment/actions.ts +3 -3
  79. package/src/comment/comment.ts +6 -9
  80. package/src/comment/render.ts +93 -47
  81. package/src/components/pagination.ts +0 -1
  82. package/src/context.ts +57 -18
  83. package/src/editor/editor.ts +111 -18
  84. package/src/editor/plugs/header-input-plug.ts +1 -1
  85. package/src/editor/plugs/upload-plug.ts +1 -1
  86. package/src/i18n/en.ts +3 -0
  87. package/src/i18n/index.ts +117 -2
  88. package/src/i18n/zh-CN.ts +4 -1
  89. package/src/layer/sidebar-layer.ts +1 -3
  90. package/src/lib/checker/admin-checker.ts +1 -1
  91. package/src/lib/checker/captcha-checker.ts +3 -3
  92. package/src/lib/stat.ts +2 -2
  93. package/src/lib/ui.ts +6 -0
  94. package/src/list/list-lite.ts +33 -25
  95. package/src/list/list.ts +3 -4
  96. package/src/main.ts +3 -0
  97. package/src/style/comment.less +2 -3
  98. package/src/style/main.less +6 -0
  99. package/test/CSRF.html +26 -0
  100. package/types/artalk-data.d.ts +3 -0
  101. package/types/context.d.ts +19 -1
  102. package/types/global.d.ts +0 -2
  103. package/vite-base.config.ts +9 -5
  104. package/vite.config.ts +5 -1
  105. package/types/index.d.ts +0 -3
@@ -0,0 +1,85 @@
1
+ import { PageData, CommentData } from '~/types/artalk-data'
2
+ import ApiBase from './api-base'
3
+
4
+ /**
5
+ * 页面 API
6
+ */
7
+ export default class PageApi extends ApiBase {
8
+ /** 页面 · 获取 */
9
+ public async pageGet(siteName?: string, offset?: number, limit?: number) {
10
+ const params: any = {
11
+ site_name: siteName || '',
12
+ offset: offset || 0,
13
+ limit: limit || 15,
14
+ }
15
+
16
+ const d = await this.POST<any>('/admin/page-get', params)
17
+ return (d as { pages: PageData[], total: number })
18
+ }
19
+
20
+ /** 页面 · 修改 */
21
+ public async pageEdit(data: PageData) {
22
+ const params: any = {
23
+ id: data.id,
24
+ key: data.key,
25
+ title: data.title,
26
+ admin_only: data.admin_only,
27
+ site_name: data.site_name || this.ctx.conf.site,
28
+ }
29
+
30
+ const d = await this.POST<any>('/admin/page-edit', params)
31
+ return (d.page as PageData)
32
+ }
33
+
34
+ /** 页面 · 删除 */
35
+ public pageDel(pageKey: string, siteName?: string) {
36
+ const params: any = {
37
+ key: String(pageKey),
38
+ site_name: siteName || '',
39
+ }
40
+
41
+ return this.POST('/admin/page-del', params)
42
+ }
43
+
44
+ /** 页面 · 数据更新 */
45
+ public async pageFetch(id?: number, siteName?: string, getStatus?: boolean) {
46
+ const params: any = {}
47
+ if (id) params.id = id
48
+ if (siteName) params.site_name = siteName
49
+ if (getStatus) params.get_status = getStatus
50
+
51
+ const d = await this.POST<any>('/admin/page-fetch', params)
52
+ return (d as any)
53
+ }
54
+
55
+ /** PV */
56
+ public async pv() {
57
+ const params: any = {
58
+ site_name: this.ctx.conf.site || '',
59
+ page_key: this.ctx.conf.pageKey || '',
60
+ page_title: this.ctx.conf.pageTitle || ''
61
+ }
62
+
63
+ const p = await this.POST<any>('/pv', params)
64
+ return p.pv as number
65
+ }
66
+
67
+ /** 统计 */
68
+ public async stat(
69
+ type: 'latest_comments'|'latest_pages'|'pv_most_pages'|'comment_most_pages'|
70
+ 'page_pv'|'site_pv'|'page_comment'|'site_comment',
71
+ pageKeys?: string|string[],
72
+ limit?: number
73
+ ) {
74
+ const params: any = {
75
+ type,
76
+ site_name: this.ctx.conf.site || '',
77
+ }
78
+
79
+ if (pageKeys) params.page_keys = Array.isArray(pageKeys) ? pageKeys.join(',') : pageKeys
80
+ if (limit) params.limit = limit
81
+
82
+ const data = await this.POST<PageData[]|CommentData[]|object|number>(`/stat`, params)
83
+ return data
84
+ }
85
+ }
@@ -9,6 +9,9 @@ export async function Fetch(ctx: Context, input: RequestInfo, init: RequestInit,
9
9
  init.headers = headers
10
10
  }
11
11
 
12
+ // 跨域请求携带 cookie (无需)
13
+ // init.credentials = 'include'
14
+
12
15
  // 请求操作
13
16
  const resp = await timeoutFetch(ctx, input, timeout || ctx.conf.reqTimeout || 15000, init)
14
17
 
@@ -60,19 +63,20 @@ export async function POST<T>(ctx: Context, url: string, data?: {[key: string]:
60
63
  const init: RequestInit = {
61
64
  method: 'POST',
62
65
  }
63
- if (data) init.body = ToFormData(data)
66
+ data = { site_name: ctx.conf.site || '', ...(data || {}) } // 总是携带 site_name
67
+ init.body = ToFormData(data)
64
68
 
65
69
  const json = await Fetch(ctx, url, init)
66
70
  return ((json.data || {}) as T)
67
71
  }
68
72
 
69
- /** 公共 GET 请求 */
70
- export async function GET<T>(ctx: Context, url: string, data?: {[key: string]: any}) {
71
- const json = await Fetch(ctx, url + (data ? (`?${new URLSearchParams(data)}`) : ''), {
72
- method: 'GET',
73
- })
74
- return ((json.data || {}) as T)
75
- }
73
+ /** 公共 GET 请求 (无 GET 方式 API) */
74
+ // export async function GET<T>(ctx: Context, url: string, data?: {[key: string]: any}) {
75
+ // const json = await Fetch(ctx, url + (data ? (`?${new URLSearchParams(data)}`) : ''), {
76
+ // method: 'GET',
77
+ // })
78
+ // return ((json.data || {}) as T)
79
+ // }
76
80
 
77
81
  /** 对象转 FormData */
78
82
  export function ToFormData(object: {[key: string]: any}): FormData {
@@ -0,0 +1,51 @@
1
+ import { SiteData } from '~/types/artalk-data'
2
+ import ApiBase from './api-base'
3
+
4
+ /**
5
+ * 站点 API
6
+ */
7
+ export default class SiteApi extends ApiBase {
8
+ /** 站点 · 获取 */
9
+ public async siteGet() {
10
+ const params: any = {}
11
+
12
+ const d = await this.POST<any>('/admin/site-get', params)
13
+ return (d.sites as SiteData[])
14
+ }
15
+
16
+ /** 站点 · 创建 */
17
+ public async siteAdd(name: string, urls: string) {
18
+ const params: any = {
19
+ name, urls,
20
+ site_name: '' // 全局保留字段,当前站点名
21
+ }
22
+
23
+ const d = await this.POST<any>('/admin/site-add', params)
24
+ return (d.site as SiteData)
25
+ }
26
+
27
+ /** 站点 · 修改 */
28
+ public async siteEdit(data: SiteData) {
29
+ const params: any = {
30
+ id: data.id,
31
+ name: data.name || '',
32
+ urls: data.urls || '',
33
+ }
34
+
35
+ const d = await this.POST<any>('/admin/site-edit', params)
36
+ return (d.site as SiteData)
37
+ }
38
+
39
+ /** 站点 · 删除 */
40
+ public siteDel(id: number, delContent = false) {
41
+ const params: any = { id, del_content: delContent }
42
+
43
+ return this.POST('/admin/site-del', params)
44
+ }
45
+
46
+ /** 导出 */
47
+ public async export() {
48
+ const d = await this.Fetch(`/admin/export`, { method: 'POST' }, 0)
49
+ return (d.data?.data || '' as string)
50
+ }
51
+ }
@@ -0,0 +1,39 @@
1
+ import ArtalkConfig from '~/types/artalk-config'
2
+ import ApiBase from './api-base'
3
+
4
+ /**
5
+ * 系统 API
6
+ */
7
+ export default class SystemApi extends ApiBase {
8
+ /** 获取配置 */
9
+ public async conf() {
10
+ const data = await this.POST<any>(`/conf`)
11
+ const conf = (data.frontend_conf || {}) as ArtalkConfig
12
+
13
+ // Patch: `emoticons` config string to json
14
+ if (conf.emoticons && typeof conf.emoticons === "string") {
15
+ conf.emoticons = conf.emoticons.trim()
16
+ if (conf.emoticons.startsWith("[") || conf.emoticons.startsWith("{")) {
17
+ conf.emoticons = JSON.parse(conf.emoticons) // pase json
18
+ } else if (conf.emoticons === "false") {
19
+ conf.emoticons = false
20
+ }
21
+ }
22
+
23
+ return conf
24
+ }
25
+
26
+ /** 获取配置数据 */
27
+ public async getSettings() {
28
+ const data = await this.POST<string>('/admin/setting-get')
29
+ return data
30
+ }
31
+
32
+ /** 保存配置数据 */
33
+ public async saveSettings(yamlStr: string) {
34
+ const data = await this.POST<boolean>('/admin/setting-save', {
35
+ data: yamlStr,
36
+ })
37
+ return data
38
+ }
39
+ }
@@ -0,0 +1,29 @@
1
+ import { ToFormData } from './request'
2
+ import ApiBase from './api-base'
3
+
4
+ /**
5
+ * 上传 API
6
+ */
7
+ export default class UploadApi extends ApiBase {
8
+ /** 图片上传 */
9
+ public async imgUpload(file: File) {
10
+ const params: any = {
11
+ name: this.ctx.user.data.nick,
12
+ email: this.ctx.user.data.email,
13
+ page_key: this.ctx.conf.pageKey,
14
+ }
15
+
16
+ if (this.ctx.conf.site) params.site_name = this.ctx.conf.site
17
+
18
+ const form = ToFormData(params)
19
+ form.set('file', file)
20
+
21
+ const init: RequestInit = {
22
+ method: 'POST',
23
+ body: form
24
+ }
25
+
26
+ const json = await this.Fetch('/img-upload', init)
27
+ return ((json.data || {}) as any) as { img_file: string, img_url: string }
28
+ }
29
+ }
@@ -0,0 +1,75 @@
1
+ import { UserData, NotifyData } from '~/types/artalk-data'
2
+ import ApiBase from './api-base'
3
+ import { ToFormData } from './request'
4
+
5
+ /**
6
+ * 用户 API
7
+ */
8
+ export default class UserApi extends ApiBase {
9
+ /** 用户 · 登录 */
10
+ public async login(name: string, email: string, password: string) {
11
+ const params: any = {
12
+ name, email, password
13
+ }
14
+
15
+ if (this.ctx.conf.site) params.site_name = this.ctx.conf.site
16
+
17
+ const data = await this.POST<any>('/login', params)
18
+ return (data.token as string)
19
+ }
20
+
21
+ /** 用户 · 获取 */
22
+ public userGet(name: string, email: string) {
23
+ const ctrl = new AbortController()
24
+ const params: any = {
25
+ name, email, site_name: (this.ctx.conf.site || '')
26
+ }
27
+
28
+ const req = this.Fetch(`/user-get`, {
29
+ method: 'POST',
30
+ body: ToFormData(params),
31
+ signal: ctrl.signal,
32
+ }).then((json) => ({
33
+ user: json.data.user as UserData|null,
34
+ is_login: json.data.is_login as boolean,
35
+ unread: (json.data.unread || []) as NotifyData[],
36
+ unread_count: json.data.unread_count || 0,
37
+ }))
38
+
39
+ return {
40
+ req,
41
+ abort: () => { ctrl.abort() },
42
+ }
43
+ }
44
+
45
+ /** 用户 · 登录状态 */
46
+ public async loginStatus() {
47
+ const data = await this.POST<any>('/login-status', {
48
+ name: this.ctx.user.data.nick,
49
+ email: this.ctx.user.data.email
50
+ })
51
+ return (data || { is_login: false, is_admin: false }) as { is_login: boolean, is_admin: boolean }
52
+ }
53
+
54
+ /** 用户 · 注销 */
55
+ public async logout() {
56
+ return this.POST('/logout')
57
+ }
58
+
59
+ /** 已读标记 */
60
+ public markRead(notifyKey: string, readAll = false) {
61
+ const params: any = {
62
+ site_name: this.ctx.conf.site || '',
63
+ notify_key: notifyKey,
64
+ }
65
+
66
+ if (readAll) {
67
+ delete params.notify_key
68
+ params.read_all = true
69
+ params.name = this.ctx.user.data.nick
70
+ params.email = this.ctx.user.data.email
71
+ }
72
+
73
+ return this.POST(`/mark-read`, params)
74
+ }
75
+ }
package/src/artalk.ts CHANGED
@@ -6,14 +6,13 @@ import ArtalkPlug from '~/types/plug'
6
6
  import Context from '~/types/context'
7
7
  import ConcreteContext from './context'
8
8
  import defaults from './defaults'
9
- import { internal as internalLocales } from './i18n'
10
9
 
11
10
  import CheckerLauncher from './lib/checker'
12
11
  import Editor from './editor'
13
12
  import List from './list'
14
13
  import SidebarLayer from './layer/sidebar-layer'
15
14
 
16
- import Layer, { GetLayerWrap } from './layer'
15
+ import Layer from './layer'
17
16
  import * as Utils from './lib/utils'
18
17
  import * as Ui from './lib/ui'
19
18
  import * as Stat from './lib/stat'
@@ -30,14 +29,14 @@ export default class Artalk {
30
29
  public ctx!: Context
31
30
  public $root!: HTMLElement
32
31
 
33
- public checkerLauncher!: CheckerLauncher
34
- public editor!: Editor
35
- public list!: List
36
- public sidebarLayer!: SidebarLayer
32
+ /** Plugins (in global scope) */
33
+ protected static Plugins: ArtalkPlug[] = [ Stat.PvCountWidget ]
37
34
 
38
- protected static Plugins: ArtalkPlug[] = [
39
- Stat.PvCountWidget
40
- ]
35
+ /** Plugins (in a instance scope) */
36
+ protected instancePlugins: ArtalkPlug[] = []
37
+
38
+ /** 禁用的组件 */
39
+ public static DisabledComponents: string[] = []
41
40
 
42
41
  constructor(customConf: Partial<ArtalkConfig>) {
43
42
  /* 初始化基本配置 */
@@ -61,32 +60,52 @@ export default class Artalk {
61
60
  this.initDarkMode()
62
61
  Utils.initMarked(this.ctx)
63
62
 
64
- // CheckerLauncher
65
- this.checkerLauncher = new CheckerLauncher(this.ctx)
66
- this.ctx.setCheckerLauncher(this.checkerLauncher)
67
-
68
- // 编辑器
69
- this.editor = new Editor(this.ctx)
70
- this.ctx.setEditor(this.editor)
71
- this.$root.appendChild(this.editor.$el)
72
-
73
- // 评论列表
74
- this.list = new List(this.ctx)
75
- this.ctx.setList(this.list)
76
- this.$root.appendChild(this.list.$el)
77
-
78
- // 侧边栏
79
- this.sidebarLayer = new SidebarLayer(this.ctx)
80
- this.ctx.setSidebarLayer(this.sidebarLayer)
81
- this.$root.appendChild(this.sidebarLayer.$el)
63
+ const Components: { [name: string]: () => void } = {
64
+ // CheckerLauncher
65
+ checkerLauncher: () => {
66
+ const checkerLauncher = new CheckerLauncher(this.ctx)
67
+ this.ctx.setCheckerLauncher(checkerLauncher)
68
+ },
69
+
70
+ // 编辑器
71
+ editor: () => {
72
+ const editor = new Editor(this.ctx)
73
+ this.ctx.setEditor(editor)
74
+ this.$root.appendChild(editor.$el)
75
+ },
76
+
77
+ // 评论列表
78
+ list: () => {
79
+ // 评论列表
80
+ const list = new List(this.ctx)
81
+ this.ctx.setList(list)
82
+ this.$root.appendChild(list.$el)
83
+
84
+ // 评论获取
85
+ list.fetchComments(0)
86
+ },
87
+
88
+ // 侧边栏 Layer
89
+ sidebarLayer: () => {
90
+ const sidebarLayer = new SidebarLayer(this.ctx)
91
+ this.ctx.setSidebarLayer(sidebarLayer)
92
+ this.$root.appendChild(sidebarLayer.$el)
93
+ },
94
+
95
+ // 默认事件绑定
96
+ eventsDefault: () => {
97
+ this.initEventBind()
98
+ }
99
+ }
82
100
 
83
- // 评论获取
84
- this.list.fetchComments(0)
101
+ // 组件初始化
102
+ Object.entries(Components).forEach(([name, initComponent]) => {
103
+ if (Artalk.DisabledComponents.includes(name)) return
85
104
 
86
- // 事件绑定初始化
87
- this.initEventBind()
105
+ initComponent()
106
+ })
88
107
 
89
- // 插件初始化
108
+ // 插件初始化 (global scope)
90
109
  Artalk.Plugins.forEach(plugin => {
91
110
  if (typeof plugin === 'function')
92
111
  plugin(this.ctx)
@@ -94,7 +113,7 @@ export default class Artalk {
94
113
  }
95
114
 
96
115
  /** 基本配置初始化 */
97
- private static HandelBaseConf(customConf: Partial<ArtalkConfig>): ArtalkConfig {
116
+ public static HandelBaseConf(customConf: Partial<ArtalkConfig>): ArtalkConfig {
98
117
  // 合并默认配置
99
118
  const conf: ArtalkConfig = Utils.mergeDeep(Artalk.defaults, customConf)
100
119
 
@@ -139,7 +158,7 @@ export default class Artalk {
139
158
  Ui.showLoading(this.$root)
140
159
  let backendConf = {}
141
160
  try {
142
- backendConf = await this.ctx.getApi().conf()
161
+ backendConf = await this.ctx.getApi().system.conf()
143
162
  } catch (err) { console.error("Load config from remote err", err) }
144
163
  this.ctx.conf = Utils.mergeDeep(this.ctx.conf, backendConf)
145
164
  Ui.hideLoading(this.$root)
@@ -149,8 +168,7 @@ export default class Artalk {
149
168
  private initEventBind() {
150
169
  // 锚点快速跳转评论
151
170
  window.addEventListener('hashchange', () => {
152
- this.list.goToCommentDelay = false
153
- this.list.checkGoToCommentByUrlHash()
171
+ this.ctx.listHashGotoCheck()
154
172
  })
155
173
 
156
174
  // 本地用户数据变更
@@ -162,20 +180,8 @@ export default class Artalk {
162
180
 
163
181
  /** 语言初始化 */
164
182
  private initLocale() {
165
- if (typeof this.conf.locale === 'string') {
166
- if (this.conf.locale === 'auto') { // 自动切换
167
- this.conf.locale = ((navigator.languages) ? navigator.languages[0] : navigator.language)
168
- }
169
-
170
- this.conf.locale = this.conf.locale.replace(
171
- /^([a-zA-Z]+)(-[a-zA-Z]+)?$/,
172
- (_, p1: string, p2: string) => (p1.toLowerCase() + (p2 || '').toUpperCase())
173
- )
174
-
175
- if (!internalLocales[this.conf.locale]) { // 语言不存在时,使用 en
176
- console.log(`Locale "${this.conf.locale}" not found.`)
177
- this.conf.locale = 'en'
178
- }
183
+ if (this.conf.locale === 'auto') { // 自适应语言
184
+ this.conf.locale = navigator.language
179
185
  }
180
186
  }
181
187
 
@@ -198,30 +204,6 @@ export default class Artalk {
198
204
  }
199
205
  }
200
206
 
201
- /** 设置暗黑模式 */
202
- public setDarkMode(darkMode: boolean) {
203
- const darkModeClassName = 'atk-dark-mode'
204
-
205
- this.ctx.conf.darkMode = darkMode
206
- this.ctx.trigger('conf-updated')
207
-
208
- if (this.conf.darkMode) {
209
- this.$root.classList.add(darkModeClassName)
210
- } else {
211
- this.$root.classList.remove(darkModeClassName)
212
- }
213
-
214
- // for Layer
215
- const { $wrap: $layerWrap } = GetLayerWrap(this.ctx)
216
- if ($layerWrap) {
217
- if (this.conf.darkMode) {
218
- $layerWrap.classList.add(darkModeClassName)
219
- } else {
220
- $layerWrap.classList.remove(darkModeClassName)
221
- }
222
- }
223
- }
224
-
225
207
  /** 监听事件 */
226
208
  public on<K extends keyof EventPayloadMap>(name: K, handler: Handler<EventPayloadMap[K]>) {
227
209
  this.ctx.on(name, handler, 'external')
@@ -242,11 +224,28 @@ export default class Artalk {
242
224
  this.ctx.listReload()
243
225
  }
244
226
 
245
- /** Use Plugin */
246
- public static Use(plugin: ArtalkPlug) {
227
+ /** 设置暗黑模式 */
228
+ public setDarkMode(darkMode: boolean) {
229
+ this.ctx.setDarkMode(darkMode)
230
+ }
231
+
232
+ /** Use Plugin (specific instance) */
233
+ public use(plugin: ArtalkPlug) {
234
+ this.instancePlugins.push(plugin)
235
+ if (typeof plugin === 'function') plugin(this.ctx)
236
+ }
237
+
238
+ /** Use Plugin (static method for global scope) */
239
+ public static use(plugin: ArtalkPlug) {
247
240
  this.Plugins.push(plugin)
248
241
  }
249
242
 
243
+ /** @deprecated Please replace it with lowercase function name `use(...)`. */
244
+ public static Use(plugin: ArtalkPlug) {
245
+ this.use(plugin)
246
+ console.warn('`Use(...)` is deprecated, replace it with lowercase `use(...)`.')
247
+ }
248
+
250
249
  /** 装载数量统计元素 */
251
250
  public static LoadCountWidget(customConf: Partial<ArtalkConfig>) {
252
251
  const conf = this.HandelBaseConf(customConf)
@@ -16,7 +16,7 @@ export default class CommentActions {
16
16
  public vote(type: 'up'|'down') {
17
17
  const actionBtn = (type === 'up') ? this.comment.getRender().voteBtnUp : this.comment.getRender().voteBtnDown
18
18
 
19
- this.ctx.getApi().vote(this.data.id, `comment_${type}`)
19
+ this.ctx.getApi().comment.vote(this.data.id, `comment_${type}`)
20
20
  .then((v) => {
21
21
  this.data.vote_up = v.up
22
22
  this.data.vote_down = v.down
@@ -45,7 +45,7 @@ export default class CommentActions {
45
45
  modify.is_pinned = !modify.is_pinned
46
46
  }
47
47
 
48
- this.ctx.getApi().commentEdit(modify).then((data) => {
48
+ this.ctx.getApi().comment.commentEdit(modify).then((data) => {
49
49
  btnElem.setLoading(false)
50
50
 
51
51
  // 刷新当前 Comment UI
@@ -64,7 +64,7 @@ export default class CommentActions {
64
64
  if (btnElem.isLoading) return // 若正在删除中
65
65
 
66
66
  btnElem.setLoading(true, `${this.ctx.$t('deleting')}...`)
67
- this.ctx.getApi().commentDel(this.data.id, this.data.site_name)
67
+ this.ctx.getApi().comment.commentDel(this.data.id, this.data.site_name)
68
68
  .then(() => {
69
69
  btnElem.setLoading(false)
70
70
  if (this.cConf.onDelete) this.cConf.onDelete(this.comment)
@@ -172,16 +172,13 @@ export default class Comment extends Component {
172
172
  return Utils.timeAgo(new Date(this.data.date), this.ctx)
173
173
  }
174
174
 
175
- /** 获取用户 UserAgent 浏览器 */
176
- public getUserUaBrowser() {
175
+ /** 获取用户 UserAgent 信息 */
176
+ public getUserUA() {
177
177
  const info = UADetect(this.data.ua)
178
- return `${info.browser} ${info.version}`
179
- }
180
-
181
- /** 获取用户 UserAgent 系统 */
182
- public getUserUaOS() {
183
- const info = UADetect(this.data.ua)
184
- return `${info.os} ${info.osVersion}`
178
+ return {
179
+ browser: `${info.browser} ${info.version}`,
180
+ os: `${info.os} ${info.osVersion}`
181
+ }
185
182
  }
186
183
 
187
184
  /** 获取配置 */