From 81bf6de60037fe8f94ebb9965281371098ce827c Mon Sep 17 00:00:00 2001 From: Liumingxun Date: Sat, 21 Sep 2024 15:18:25 +0800 Subject: [PATCH 1/6] feat(plugin/page_vote): add `page_vote` plugin --- ui/artalk/index.html | 1 + ui/artalk/src/defaults.ts | 2 + ui/artalk/src/plugins/index.ts | 2 + ui/artalk/src/plugins/page-vote.ts | 61 ++++++++++++++++++++++++++++++ ui/artalk/src/types/config.ts | 12 ++++++ 5 files changed, 78 insertions(+) create mode 100644 ui/artalk/src/plugins/page-vote.ts diff --git a/ui/artalk/index.html b/ui/artalk/index.html index c6f89934..498b1064 100644 --- a/ui/artalk/index.html +++ b/ui/artalk/index.html @@ -139,6 +139,7 @@ | +
{ + ctx.watchConf(['pageKey', 'pageVote'], (conf) => { + const defaultOptions = { + getApi: () => ctx.getApi(), + pageKey: conf.pageKey, + vote: true, + voteDown: false, + btnEl: '.artalk-page-vote', + el: '.artalk-page-vote-count', + } + + if (!conf.pageVote) return + const pageVoteConfig = typeof conf.pageVote === 'object' ? conf.pageVote : {} + initPageVoteWidget(ctx, { ...defaultOptions, ...pageVoteConfig }) + }) +} + +function initPageVoteWidget(ctx: ContextApi, options: PageVoteOptions) { + if (!options.vote) return + + const btnContainer = document.querySelector(options.btnEl) as HTMLElement + if (!btnContainer) throw Error(`page vote's config \`btnEl\` selector ${options.btnEl} not found`) + + const api = options.getApi() + + ctx.on('list-fetched', ({ data }) => { + const voteUpBtn = new ActionBtn(() => `${$t('voteUp')}${data!.page.vote_up || 0}`).appendTo(btnContainer) + voteUpBtn.setClick(() => { + api.votes.vote('page_up', data!.page.id, { + ...api.getUserFields() + }).then(res => { + // todo: update vote count + console.log(res) + }) + }) + + if (options.voteDown) { + const voteDownBtn = new ActionBtn(() => `${$t('voteDown')}`).appendTo(btnContainer) + voteDownBtn.setClick(() => { + api.votes.vote('page_down', data!.page.id, { + ...api.getUserFields() + }) + }) + } + }) +} diff --git a/ui/artalk/src/types/config.ts b/ui/artalk/src/types/config.ts index 74c72bd3..861c11e2 100644 --- a/ui/artalk/src/types/config.ts +++ b/ui/artalk/src/types/config.ts @@ -73,6 +73,18 @@ export interface ArtalkConfig { /** 评论投票反对按钮 */ voteDown: boolean + /** 页面投票按钮 */ + pageVote?: { + /** 页面投票反对按钮 */ + voteDown: boolean + + /** 页面投票按钮绑定 Selector */ + btnEl: string + + /** 页面投票数绑定 Selector */ + el: string + } | boolean + /** 评论预览功能 */ preview: boolean From d991186cf06eae1b953fe07f6913d0d617b5cdca Mon Sep 17 00:00:00 2001 From: Liumingxun Date: Sat, 21 Sep 2024 20:04:21 +0800 Subject: [PATCH 2/6] feat(plugin/page_vote): update vote count text adjust code structure disable `page vote` plugin by default --- ui/artalk/src/defaults.ts | 2 -- ui/artalk/src/plugins/page-vote.ts | 34 ++++++++++++++++++------------ 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/ui/artalk/src/defaults.ts b/ui/artalk/src/defaults.ts index d5f9a027..83ab79be 100644 --- a/ui/artalk/src/defaults.ts +++ b/ui/artalk/src/defaults.ts @@ -19,8 +19,6 @@ const defaults: ArtalkConfig = { emoticons: 'https://cdn.jsdelivr.net/gh/ArtalkJS/Emoticons/grps/default.json', - pageVote: true, - vote: true, voteDown: false, uaBadge: true, diff --git a/ui/artalk/src/plugins/page-vote.ts b/ui/artalk/src/plugins/page-vote.ts index 98f166fa..8646ecc0 100644 --- a/ui/artalk/src/plugins/page-vote.ts +++ b/ui/artalk/src/plugins/page-vote.ts @@ -1,11 +1,8 @@ import ActionBtn from '../components/action-btn' import type { ArtalkPlugin, ContextApi } from '@/types' -import { Api } from '@/api' import $t from '@/i18n' -export interface PageVoteOptions { - getApi(): Api - +interface PageVoteOptions { pageKey: string vote: boolean voteDown: boolean @@ -15,8 +12,9 @@ export interface PageVoteOptions { export const PageVoteWidget: ArtalkPlugin = (ctx) => { ctx.watchConf(['pageKey', 'pageVote'], (conf) => { + if (!conf.pageVote) return + const defaultOptions = { - getApi: () => ctx.getApi(), pageKey: conf.pageKey, vote: true, voteDown: false, @@ -24,36 +22,44 @@ export const PageVoteWidget: ArtalkPlugin = (ctx) => { el: '.artalk-page-vote-count', } - if (!conf.pageVote) return const pageVoteConfig = typeof conf.pageVote === 'object' ? conf.pageVote : {} initPageVoteWidget(ctx, { ...defaultOptions, ...pageVoteConfig }) }) } function initPageVoteWidget(ctx: ContextApi, options: PageVoteOptions) { - if (!options.vote) return - const btnContainer = document.querySelector(options.btnEl) as HTMLElement if (!btnContainer) throw Error(`page vote's config \`btnEl\` selector ${options.btnEl} not found`) - const api = options.getApi() + const api = ctx.getApi() ctx.on('list-fetched', ({ data }) => { - const voteUpBtn = new ActionBtn(() => `${$t('voteUp')}${data!.page.vote_up || 0}`).appendTo(btnContainer) + const voteUpBtn = new ActionBtn(() => `${$t('voteUp')} (${data!.page.vote_up || 0})`).appendTo(btnContainer) voteUpBtn.setClick(() => { api.votes.vote('page_up', data!.page.id, { ...api.getUserFields() - }).then(res => { - // todo: update vote count - console.log(res) + }).then(({ data: { up, down} }) => { + data!.page.vote_up = up + data!.page.vote_down = down + voteUpBtn.updateText() + }).catch((err) => { + voteUpBtn.setError($t('voteFail')) + console.error(err) }) }) if (options.voteDown) { - const voteDownBtn = new ActionBtn(() => `${$t('voteDown')}`).appendTo(btnContainer) + const voteDownBtn = new ActionBtn(() => `${$t('voteDown')} (${data!.page.vote_down || 0})`).appendTo(btnContainer) voteDownBtn.setClick(() => { api.votes.vote('page_down', data!.page.id, { ...api.getUserFields() + }).then(({ data: { up, down } }) => { + data!.page.vote_up = up + data!.page.vote_down = down + voteUpBtn.updateText() + }).catch((err) => { + voteDownBtn.setError($t('voteFail')) + console.error(err) }) }) } From 8d1f055c3944a26a649f2f1a97f5cafcc8d80068 Mon Sep 17 00:00:00 2001 From: Liumingxun Date: Sun, 22 Sep 2024 13:59:02 +0800 Subject: [PATCH 3/6] feat(plugin/page_vote): reuse actions btn style --- ui/artalk/src/defaults.ts | 2 ++ ui/artalk/src/plugins/page-vote.ts | 5 +++-- ui/artalk/src/style/_action-btn.scss | 21 +++++++++++++++++++++ ui/artalk/src/style/comment.scss | 22 +++------------------- ui/artalk/src/style/main.scss | 4 ++++ 5 files changed, 33 insertions(+), 21 deletions(-) create mode 100644 ui/artalk/src/style/_action-btn.scss diff --git a/ui/artalk/src/defaults.ts b/ui/artalk/src/defaults.ts index 83ab79be..d5f9a027 100644 --- a/ui/artalk/src/defaults.ts +++ b/ui/artalk/src/defaults.ts @@ -19,6 +19,8 @@ const defaults: ArtalkConfig = { emoticons: 'https://cdn.jsdelivr.net/gh/ArtalkJS/Emoticons/grps/default.json', + pageVote: true, + vote: true, voteDown: false, uaBadge: true, diff --git a/ui/artalk/src/plugins/page-vote.ts b/ui/artalk/src/plugins/page-vote.ts index 8646ecc0..e09794a9 100644 --- a/ui/artalk/src/plugins/page-vote.ts +++ b/ui/artalk/src/plugins/page-vote.ts @@ -29,7 +29,8 @@ export const PageVoteWidget: ArtalkPlugin = (ctx) => { function initPageVoteWidget(ctx: ContextApi, options: PageVoteOptions) { const btnContainer = document.querySelector(options.btnEl) as HTMLElement - if (!btnContainer) throw Error(`page vote's config \`btnEl\` selector ${options.btnEl} not found`) + if (!btnContainer) return // throw Error(`page vote's config \`btnEl\` selector ${options.btnEl} not found`) + btnContainer.classList.add('atk-layer-wrap') const api = ctx.getApi() @@ -38,7 +39,7 @@ function initPageVoteWidget(ctx: ContextApi, options: PageVoteOptions) { voteUpBtn.setClick(() => { api.votes.vote('page_up', data!.page.id, { ...api.getUserFields() - }).then(({ data: { up, down} }) => { + }).then(({ data: { up, down } }) => { data!.page.vote_up = up data!.page.vote_down = down voteUpBtn.updateText() diff --git a/ui/artalk/src/style/_action-btn.scss b/ui/artalk/src/style/_action-btn.scss new file mode 100644 index 00000000..f5160f91 --- /dev/null +++ b/ui/artalk/src/style/_action-btn.scss @@ -0,0 +1,21 @@ +%action-btn { + color: var(--at-color-meta); + font-size: 13px; + line-height: 25px; + display: inline-flex; + cursor: pointer; + user-select: none; + + &.atk-error, + &.atk-error:hover { + color: var(--at-color-red); + } + + &:not(:last-child):not(.atk-hide) { + margin-right: 16px; + } + + &:hover { + color: var(--at-color-deep); + } +} \ No newline at end of file diff --git a/ui/artalk/src/style/comment.scss b/ui/artalk/src/style/comment.scss index d430c0f5..66a2b0d0 100644 --- a/ui/artalk/src/style/comment.scss +++ b/ui/artalk/src/style/comment.scss @@ -1,3 +1,5 @@ +@import 'action-btn'; + .atk-comment-wrap { overflow: hidden; position: relative; @@ -263,25 +265,7 @@ flex-wrap: wrap; & > span { - color: var(--at-color-meta); - font-size: 13px; - line-height: 25px; - display: inline-flex; - cursor: pointer; - user-select: none; - - &.atk-error, - &.atk-error:hover { - color: var(--at-color-red); - } - - &:not(:last-child):not(.atk-hide) { - margin-right: 16px; - } - - &:hover { - color: var(--at-color-deep); - } + @extend %action-btn; } } } diff --git a/ui/artalk/src/style/main.scss b/ui/artalk/src/style/main.scss index e26d924b..6ec568fd 100644 --- a/ui/artalk/src/style/main.scss +++ b/ui/artalk/src/style/main.scss @@ -1,3 +1,4 @@ +@import 'action-btn'; @import './_color.scss'; @import './comment.scss'; @import './list.scss'; @@ -542,6 +543,9 @@ /* Common Action Btn */ .atk-common-action-btn { + + @extend %action-btn; + &.atk-btn-confirm { color: var(--at-color-yellow) !important; } From 60e9eb7c5073db9cb9086d1fc3d4d09234788052 Mon Sep 17 00:00:00 2001 From: Liumingxun Date: Sun, 22 Sep 2024 14:55:19 +0800 Subject: [PATCH 4/6] perf(plugin/page-vote): make type strong --- ui/artalk/src/plugins/page-vote.ts | 29 +++++++++++++++++------------ ui/artalk/src/types/config.ts | 5 +---- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/ui/artalk/src/plugins/page-vote.ts b/ui/artalk/src/plugins/page-vote.ts index e09794a9..38887812 100644 --- a/ui/artalk/src/plugins/page-vote.ts +++ b/ui/artalk/src/plugins/page-vote.ts @@ -7,7 +7,6 @@ interface PageVoteOptions { vote: boolean voteDown: boolean btnEl: string - el: string } export const PageVoteWidget: ArtalkPlugin = (ctx) => { @@ -19,7 +18,6 @@ export const PageVoteWidget: ArtalkPlugin = (ctx) => { vote: true, voteDown: false, btnEl: '.artalk-page-vote', - el: '.artalk-page-vote-count', } const pageVoteConfig = typeof conf.pageVote === 'object' ? conf.pageVote : {} @@ -28,36 +26,43 @@ export const PageVoteWidget: ArtalkPlugin = (ctx) => { } function initPageVoteWidget(ctx: ContextApi, options: PageVoteOptions) { - const btnContainer = document.querySelector(options.btnEl) as HTMLElement + const btnContainer = document.querySelector(options.btnEl) if (!btnContainer) return // throw Error(`page vote's config \`btnEl\` selector ${options.btnEl} not found`) btnContainer.classList.add('atk-layer-wrap') const api = ctx.getApi() ctx.on('list-fetched', ({ data }) => { - const voteUpBtn = new ActionBtn(() => `${$t('voteUp')} (${data!.page.vote_up || 0})`).appendTo(btnContainer) + if (!data) return + + const voteUpBtn = new ActionBtn(() => `${$t('voteUp')} (${data.page.vote_up || 0})`).appendTo(btnContainer) + const voteDownBtn = options.voteDown + ? new ActionBtn(() => `${$t('voteDown')} (${data.page.vote_down || 0})`).appendTo(btnContainer) + : null + voteUpBtn.setClick(() => { - api.votes.vote('page_up', data!.page.id, { + api.votes.vote('page_up', data.page.id, { ...api.getUserFields() }).then(({ data: { up, down } }) => { - data!.page.vote_up = up - data!.page.vote_down = down + data.page.vote_up = up + data.page.vote_down = down voteUpBtn.updateText() + voteDownBtn?.updateText() }).catch((err) => { voteUpBtn.setError($t('voteFail')) console.error(err) }) }) - if (options.voteDown) { - const voteDownBtn = new ActionBtn(() => `${$t('voteDown')} (${data!.page.vote_down || 0})`).appendTo(btnContainer) + if (voteDownBtn) { voteDownBtn.setClick(() => { - api.votes.vote('page_down', data!.page.id, { + api.votes.vote('page_down', data.page.id, { ...api.getUserFields() }).then(({ data: { up, down } }) => { - data!.page.vote_up = up - data!.page.vote_down = down + data.page.vote_up = up + data.page.vote_down = down voteUpBtn.updateText() + voteDownBtn.updateText() }).catch((err) => { voteDownBtn.setError($t('voteFail')) console.error(err) diff --git a/ui/artalk/src/types/config.ts b/ui/artalk/src/types/config.ts index 861c11e2..177efede 100644 --- a/ui/artalk/src/types/config.ts +++ b/ui/artalk/src/types/config.ts @@ -79,10 +79,7 @@ export interface ArtalkConfig { voteDown: boolean /** 页面投票按钮绑定 Selector */ - btnEl: string - - /** 页面投票数绑定 Selector */ - el: string + btnEl?: string } | boolean /** 评论预览功能 */ From 6a4e9c0eb7db6cccb2aad023626f4746df474dbc Mon Sep 17 00:00:00 2001 From: Liumingxun Date: Sun, 22 Sep 2024 15:18:02 +0800 Subject: [PATCH 5/6] feat: watch `darkMode` conf --- ui/artalk/src/plugins/page-vote.ts | 6 ++++-- ui/artalk/src/style/_color.scss | 3 ++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/ui/artalk/src/plugins/page-vote.ts b/ui/artalk/src/plugins/page-vote.ts index 38887812..abfea204 100644 --- a/ui/artalk/src/plugins/page-vote.ts +++ b/ui/artalk/src/plugins/page-vote.ts @@ -10,7 +10,7 @@ interface PageVoteOptions { } export const PageVoteWidget: ArtalkPlugin = (ctx) => { - ctx.watchConf(['pageKey', 'pageVote'], (conf) => { + ctx.watchConf(['pageKey', 'pageVote', 'darkMode'], (conf) => { if (!conf.pageVote) return const defaultOptions = { @@ -28,7 +28,9 @@ export const PageVoteWidget: ArtalkPlugin = (ctx) => { function initPageVoteWidget(ctx: ContextApi, options: PageVoteOptions) { const btnContainer = document.querySelector(options.btnEl) if (!btnContainer) return // throw Error(`page vote's config \`btnEl\` selector ${options.btnEl} not found`) - btnContainer.classList.add('atk-layer-wrap') + btnContainer.classList.add('atk-page-vote') + if (ctx.getConf().darkMode) btnContainer.classList.add('atk-dark-mode') + else btnContainer.classList.remove('atk-dark-mode') const api = ctx.getApi() diff --git a/ui/artalk/src/style/_color.scss b/ui/artalk/src/style/_color.scss index 7f1b4999..5d2a6f42 100644 --- a/ui/artalk/src/style/_color.scss +++ b/ui/artalk/src/style/_color.scss @@ -1,5 +1,6 @@ .artalk, -.atk-layer-wrap { +.atk-layer-wrap, +.atk-page-vote { --at-color-font: #2a2e2e; --at-color-deep: #2a2e2e; --at-color-sub: #757575; From e9c47301872998684d695a7521170959cc75df87 Mon Sep 17 00:00:00 2001 From: Liumingxun Date: Sun, 22 Sep 2024 16:25:56 +0800 Subject: [PATCH 6/6] doc(plugin/page_vote): add config doc --- docs/docs/en/guide/frontend/config.md | 9 +++++++++ docs/docs/zh/guide/frontend/config.md | 9 +++++++++ 2 files changed, 18 insertions(+) diff --git a/docs/docs/en/guide/frontend/config.md b/docs/docs/en/guide/frontend/config.md index 385ebff8..a1726df8 100644 --- a/docs/docs/en/guide/frontend/config.md +++ b/docs/docs/en/guide/frontend/config.md @@ -278,6 +278,15 @@ When querying comment counts and page views, Artalk's statistical component uses To facilitate theme adaptation, you can customize the attribute name as needed, for example, replacing it with `data-path`, the HTML tag would be ``. +### pageVote + +**Page Voting Buttons** + +- Type: `Boolean` | `{ voteDown: boolean, btnEl?: string }` +- Default: `true` + +Enable / disable page voting feature, set to `falsy` value to disable. `voteDown` sets whether to enable the downvote button (hidden by default). `btnEl` is the selector of the vote button container, defaulting to `".artalk-page-vote"`. + ### vote **Voting Buttons** diff --git a/docs/docs/zh/guide/frontend/config.md b/docs/docs/zh/guide/frontend/config.md index 31abd384..69313d2c 100644 --- a/docs/docs/zh/guide/frontend/config.md +++ b/docs/docs/zh/guide/frontend/config.md @@ -278,6 +278,15 @@ Artalk 统计组件查询评论数和浏览量时,会通过该属性名来查 为了便于主题适配,可根据需要自定义属性名,例如将其替换为 `data-path`,则 HTML 标签为 ``。 +### pageVote + +**页面投票** + +- 类型:`Boolean` | `{ voteDown: boolean, btnEl?: string }` +- 默认值: `true` + +启用 / 禁用页面投票功能,设置为 `Falsy` 值则不启用该功能。 `voteDown` 设置是否启用反对投票按钮(默认隐藏), `btnEl` 为投票按钮的容器选择器,默认为 `".artalk-page-vote"`。 + ### vote **投票按钮**