From 7d7193cb63ac8811152707a6896e5cf7ae05258c Mon Sep 17 00:00:00 2001 From: syuilo <syuilotan@yahoo.co.jp> Date: Mon, 11 Jun 2018 11:24:29 +0900 Subject: [PATCH] :v: --- locales/ja.yml | 3 + .../common/views/widgets/hashtags.chart.vue | 78 +++++++++++++++++++ .../app/common/views/widgets/hashtags.vue | 57 +++++++------- src/server/api/endpoints/hashtags/trend.ts | 12 ++- 4 files changed, 119 insertions(+), 31 deletions(-) create mode 100644 src/client/app/common/views/widgets/hashtags.chart.vue diff --git a/locales/ja.yml b/locales/ja.yml index 64ee00dba..da751c9e3 100644 --- a/locales/ja.yml +++ b/locales/ja.yml @@ -254,6 +254,9 @@ common/views/widgets/posts-monitor.vue: title: "投稿チャート" toggle: "表示を切り替え" +common/views/widgets/hashtags.vue: + title: "ハッシュタグ" + common/views/widgets/server.vue: title: "サーバー情報" toggle: "表示を切り替え" diff --git a/src/client/app/common/views/widgets/hashtags.chart.vue b/src/client/app/common/views/widgets/hashtags.chart.vue new file mode 100644 index 000000000..19b56ef28 --- /dev/null +++ b/src/client/app/common/views/widgets/hashtags.chart.vue @@ -0,0 +1,78 @@ +<template> +<svg :viewBox="`0 0 ${ viewBoxX } ${ viewBoxY }`"> + <defs> + <linearGradient :id="gradientId" x1="0" x2="0" y1="1" y2="0"> + <stop offset="0%" stop-color="hsl(200, 80%, 70%)"></stop> + <stop offset="100%" stop-color="hsl(90, 80%, 70%)"></stop> + </linearGradient> + <mask :id="maskId" x="0" y="0" :width="viewBoxX" :height="viewBoxY"> + <polygon + :points="polygonPoints" + fill="#fff" + fill-opacity="0.5"/> + <polyline + :points="polylinePoints" + fill="none" + stroke="#fff" + stroke-width="0.7"/> + <circle + :cx="headX" + :cy="headY" + r="1.2" + fill="#fff"/> + </mask> + </defs> + <rect + x="-2" y="-2" + :width="viewBoxX + 4" :height="viewBoxY + 4" + :style="`stroke: none; fill: url(#${ gradientId }); mask: url(#${ maskId })`"/> +</svg> +</template> + +<script lang="ts"> +import Vue from 'vue'; +import * as uuid from 'uuid'; + +export default Vue.extend({ + props: { + src: { + type: Array, + required: true + } + }, + data() { + return { + viewBoxX: 50, + viewBoxY: 30, + gradientId: uuid(), + maskId: uuid(), + polylinePoints: '', + polygonPoints: '', + headX: null, + headY: null + }; + }, + watch: { + src() { + this.draw(); + } + }, + created() { + this.draw(); + }, + methods: { + draw() { + const stats = this.src.slice().reverse(); + const peak = Math.max.apply(null, stats) || 1; + + const polylinePoints = stats.map((x, i) => [this.viewBoxX - ((stats.length - 1) - i), (1 - (x / peak)) * this.viewBoxY]); + this.polylinePoints = polylinePoints.map(xy => `${xy[0]},${xy[1]}`).join(' '); + + this.polygonPoints = `${this.viewBoxX - (stats.length - 1)},${ this.viewBoxY } ${ this.polylinePoints } ${ this.viewBoxX },${ this.viewBoxY }`; + + this.headX = polylinePoints[polylinePoints.length - 1][0]; + this.headY = polylinePoints[polylinePoints.length - 1][1]; + } + } +}); +</script> diff --git a/src/client/app/common/views/widgets/hashtags.vue b/src/client/app/common/views/widgets/hashtags.vue index 0ac62af70..c4647ee0f 100644 --- a/src/client/app/common/views/widgets/hashtags.vue +++ b/src/client/app/common/views/widgets/hashtags.vue @@ -6,7 +6,12 @@ <div class="mkw-hashtags--body" :data-mobile="platform == 'mobile'"> <p class="fetching" v-if="fetching">%fa:spinner .pulse .fw%%i18n:common.loading%<mk-ellipsis/></p> <div v-else> - <router-link v-for="stat in stats" :key="stat.tag" :to="`/tags/${ stat.tag }`">{{ stat.tag }}</router-link> + <div v-for="stat in stats" :key="stat.tag"> + <div class="tag"> + <router-link :to="`/tags/${ stat.tag }`">#{{ stat.tag }}</router-link> + </div> + <x-chart class="chart" :src="stat.chart"/> + </div> </div> </div> </mk-widget-container> @@ -15,12 +20,17 @@ <script lang="ts"> import define from '../../../common/define-widget'; +import XChart from './hashtags.chart.vue'; + export default define({ name: 'hashtags', props: () => ({ compact: false }) }).extend({ + components: { + XChart + }, data() { return { stats: [], @@ -52,21 +62,8 @@ export default define({ <style lang="stylus" scoped> root(isDark) - .mkw-rss--body - .feed - padding 12px 16px - font-size 0.9em - - > a - display block - padding 4px 0 - color isDark ? #9aa4b3 : #666 - border-bottom dashed 1px isDark ? #1c2023 : #eee - - &:last-child - border-bottom none - - .fetching + .mkw-hashtags--body + > .fetching margin 0 padding 16px text-align center @@ -75,23 +72,29 @@ root(isDark) > [data-fa] margin-right 4px - &[data-mobile] - background isDark ? #21242f : #f3f3f3 + > div + > div + display flex + align-items center + padding 16px - .feed - padding 0 + &:not(:last-child) + border-bottom solid 1px #393f4f - > a - padding 8px 16px - border-bottom none + > .tag + flex 1 - &:nth-child(even) - background isDark ? rgba(#000, 0.05) : rgba(#fff, 0.7) + > a + color #9baec8 -.mkw-rss[data-darkmode] + > .chart + width 50px + height 30px + +.mkw-hashtags[data-darkmode] root(true) -.mkw-rss:not([data-darkmode]) +.mkw-hashtags:not([data-darkmode]) root(false) </style> diff --git a/src/server/api/endpoints/hashtags/trend.ts b/src/server/api/endpoints/hashtags/trend.ts index 3b95e1fa4..6a49fec3d 100644 --- a/src/server/api/endpoints/hashtags/trend.ts +++ b/src/server/api/endpoints/hashtags/trend.ts @@ -4,13 +4,10 @@ import Note from '../../../../models/note'; * Get trends of hashtags */ module.exports = (params, user) => new Promise(async (res, rej) => { - // 10分 - const interval = 1000 * 60 * 10; - const data = await Note.aggregate([{ $match: { createdAt: { - $gt: new Date(Date.now() - interval) + $gt: new Date(Date.now() - 1000 * 60 * 60) }, tags: { $exists: true, @@ -48,6 +45,10 @@ module.exports = (params, user) => new Promise(async (res, rej) => { }> }>; + if (data.length == 0) { + return res([]); + } + const hots = data[0].tags .sort((a, b) => a.count - b.count) .map(tag => tag.tag) @@ -56,6 +57,9 @@ module.exports = (params, user) => new Promise(async (res, rej) => { const countPromises: Array<Promise<number[]>> = []; for (let i = 0; i < 10; i++) { + // 10分 + const interval = 1000 * 60 * 10; + countPromises.push(Promise.all(hots.map(tag => Note.count({ tags: tag, createdAt: {