diff --git a/src/client/app/desktop/views/pages/deck/deck.hashtag-column.vue b/src/client/app/desktop/views/pages/deck/deck.hashtag-column.vue index 70058665e..5d8c25676 100644 --- a/src/client/app/desktop/views/pages/deck/deck.hashtag-column.vue +++ b/src/client/app/desktop/views/pages/deck/deck.hashtag-column.vue @@ -4,7 +4,10 @@ %fa:hashtag%{{ tag }} - +
+
+ +
@@ -12,6 +15,8 @@ import Vue from 'vue'; import XColumn from './deck.column.vue'; import XHashtagTl from './deck.hashtag-tl.vue'; +import * as G2 from '@antv/g2'; +import * as tinycolor from 'tinycolor2'; export default Vue.extend({ components: { @@ -32,6 +37,67 @@ export default Vue.extend({ query: [[this.tag]] }; } + }, + + mounted() { + (this as any).api('charts/hashtag', { + tag: this.tag, + span: 'hour', + limit: 30 + }).then(stats => { + const data = []; + + const now = new Date(); + const y = now.getFullYear(); + const m = now.getMonth(); + const d = now.getDate(); + const h = now.getHours(); + + for (let i = 0; i < 30; i++) { + const x = new Date(y, m, d, h - i + 1); + data.push({ + x: x, + count: stats.count[i] + }); + } + + const chart = new G2.Chart({ + container: this.$refs.chart as HTMLDivElement, + forceFit: true, + height: 70, + padding: 8, + renderer: 'svg' + }); + + const text = tinycolor(getComputedStyle(document.documentElement).getPropertyValue('--primary')); + + chart.area().position('x*count').color(`l(100) 0:${text.clone().setAlpha(0.5).toRgbString()} 1:${text.clone().setAlpha(0.25).toRgbString()}`); + chart.line().position('x*count').color(`#${text.clone().toHex()}`).size(2); + chart.legend(false); + chart.axis('x', false); + chart.axis('count', false); + chart.tooltip(true, { + showTitle: false, + crosshairs: { + type: 'line' + } + }); + chart.source(data); + chart.render(); + }); } }); + + diff --git a/src/server/api/endpoints/charts/hashtag.ts b/src/server/api/endpoints/charts/hashtag.ts new file mode 100644 index 000000000..b42bc97ef --- /dev/null +++ b/src/server/api/endpoints/charts/hashtag.ts @@ -0,0 +1,39 @@ +import $ from 'cafy'; +import getParams from '../../get-params'; +import { hashtagStats } from '../../../../services/stats'; + +export const meta = { + desc: { + 'ja-JP': 'ハッシュタグごとの統計を取得します。' + }, + + params: { + span: $.str.or(['day', 'hour']).note({ + desc: { + 'ja-JP': '集計のスパン (day または hour)' + } + }), + + limit: $.num.optional.range(1, 100).note({ + default: 30, + desc: { + 'ja-JP': '最大数。例えば 30 を指定したとすると、スパンが"day"の場合は30日分のデータが、スパンが"hour"の場合は30時間分のデータが返ります。' + } + }), + + tag: $.str.note({ + desc: { + 'ja-JP': '対象のハッシュタグ' + } + }), + } +}; + +export default (params: any) => new Promise(async (res, rej) => { + const [ps, psErr] = getParams(meta, params); + if (psErr) throw psErr; + + const stats = await hashtagStats.getChart(ps.span as any, ps.limit, ps.tag); + + res(stats); +});