From 978282404a34f117611ec5d3135227a084d98730 Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 3 Feb 2022 01:45:34 +0900 Subject: fix(client): fix compare-versions import --- packages/client/src/init.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'packages/client/src') diff --git a/packages/client/src/init.ts b/packages/client/src/init.ts index 81e41febd1..994177468f 100644 --- a/packages/client/src/init.ts +++ b/packages/client/src/init.ts @@ -14,7 +14,7 @@ if (localStorage.getItem('accounts') != null) { //#endregion import { computed, createApp, watch, markRaw, version as vueVersion } from 'vue'; -import * as compareVersions from 'compare-versions'; +import compareVersions from 'compare-versions'; import widgets from '@/widgets'; import directives from '@/directives'; -- cgit v1.2.3-freya From 5e198381d32e45c60a210b29aa0269d01e1d9c6f Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 3 Feb 2022 02:08:54 +0900 Subject: :art: --- packages/client/src/components/chart.vue | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'packages/client/src') diff --git a/packages/client/src/components/chart.vue b/packages/client/src/components/chart.vue index 3e46c51b47..e9938e6903 100644 --- a/packages/client/src/components/chart.vue +++ b/packages/client/src/components/chart.vue @@ -250,7 +250,15 @@ export default defineComponent({ }, interaction: { intersect: false, + mode: 'index', }, + elements: { + point: { + hoverRadius: 5, + hoverBorderWidth: 2, + }, + }, + animation: false, plugins: { legend: { display: props.detailed, -- cgit v1.2.3-freya From d31945e1fd692ce657e18cf7349e89bef5f46d0e Mon Sep 17 00:00:00 2001 From: tamaina Date: Fri, 4 Feb 2022 08:39:20 +0900 Subject: fix: タイムライン種別を切り替えると「新しいノートがあります」の表示が残留してしまうのを修正 (#8250) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix #6831 --- packages/client/src/pages/timeline.vue | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) (limited to 'packages/client/src') diff --git a/packages/client/src/pages/timeline.vue b/packages/client/src/pages/timeline.vue index b2266d22c3..79f00c4b44 100644 --- a/packages/client/src/pages/timeline.vue +++ b/packages/client/src/pages/timeline.vue @@ -46,8 +46,10 @@ const keymap = { const tlComponent = $ref>(); const rootEl = $ref(); -let src = $ref<'home' | 'local' | 'social' | 'global'>(defaultStore.state.tl.src); let queue = $ref(0); +const src = $computed(() => defaultStore.reactiveState.tl.value.src); + +watch ($$(src), () => queue = 0); function queueUpdated(q: number): void { queue = q; @@ -60,7 +62,7 @@ function top(): void { async function chooseList(ev: MouseEvent): Promise { const lists = await os.api('users/lists/list'); const items = lists.map(list => ({ - type: 'link', + type: 'link' as const, text: list.name, to: `/timeline/list/${list.id}`, })); @@ -70,7 +72,7 @@ async function chooseList(ev: MouseEvent): Promise { async function chooseAntenna(ev: MouseEvent): Promise { const antennas = await os.api('antennas/list'); const items = antennas.map(antenna => ({ - type: 'link', + type: 'link' as const, text: antenna.name, indicate: antenna.hasUnreadNote, to: `/timeline/antenna/${antenna.id}`, @@ -81,7 +83,7 @@ async function chooseAntenna(ev: MouseEvent): Promise { async function chooseChannel(ev: MouseEvent): Promise { const channels = await os.api('channels/followed'); const items = channels.map(channel => ({ - type: 'link', + type: 'link' as const, text: channel.name, indicate: channel.hasUnreadNote, to: `/channels/${channel.id}`, @@ -89,9 +91,10 @@ async function chooseChannel(ev: MouseEvent): Promise { os.popupMenu(items, ev.currentTarget ?? ev.target); } -function saveSrc(): void { +function saveSrc(newSrc: 'home' | 'local' | 'social' | 'global'): void { defaultStore.set('tl', { - src: src, + ...defaultStore.state.tl, + src: newSrc, }); } @@ -135,25 +138,25 @@ defineExpose({ title: i18n.ts._timelines.home, icon: 'fas fa-home', iconOnly: true, - onClick: () => { src = 'home'; saveSrc(); }, + onClick: () => { saveSrc('home'); }, }, ...(isLocalTimelineAvailable ? [{ active: src === 'local', title: i18n.ts._timelines.local, icon: 'fas fa-comments', iconOnly: true, - onClick: () => { src = 'local'; saveSrc(); }, + onClick: () => { saveSrc('local'); }, }, { active: src === 'social', title: i18n.ts._timelines.social, icon: 'fas fa-share-alt', iconOnly: true, - onClick: () => { src = 'social'; saveSrc(); }, + onClick: () => { saveSrc('social'); }, }] : []), ...(isGlobalTimelineAvailable ? [{ active: src === 'global', title: i18n.ts._timelines.global, icon: 'fas fa-globe', iconOnly: true, - onClick: () => { src = 'global'; saveSrc(); }, + onClick: () => { saveSrc('global'); }, }] : [])], })), }); -- cgit v1.2.3-freya From 0b462feff6be22f0da8ea773a2a5af24d6300240 Mon Sep 17 00:00:00 2001 From: syuilo Date: Fri, 4 Feb 2022 16:39:09 +0900 Subject: enhance(client): improve chart rendering --- packages/client/src/components/chart.vue | 27 +++++++++++++++++++++-- packages/client/src/pages/settings/drive.vue | 6 ++--- packages/client/src/pages/user/index.activity.vue | 2 +- 3 files changed, 29 insertions(+), 6 deletions(-) (limited to 'packages/client/src') diff --git a/packages/client/src/components/chart.vue b/packages/client/src/components/chart.vue index e9938e6903..ea0df55355 100644 --- a/packages/client/src/components/chart.vue +++ b/packages/client/src/components/chart.vue @@ -95,6 +95,11 @@ export default defineComponent({ required: false, default: false }, + bar: { + type: Boolean, + required: false, + default: false + }, aspectRatio: { type: Number, required: false, @@ -187,7 +192,7 @@ export default defineComponent({ Chart.defaults.color = getComputedStyle(document.documentElement).getPropertyValue('--fg'); chartInstance = new Chart(chartEl.value, { - type: 'line', + type: props.bar ? 'bar' : 'line', data: { labels: new Array(props.limit).fill(0).map((_, i) => getDate(i).toLocaleString()).slice().reverse(), datasets: data.series.map((x, i) => ({ @@ -195,12 +200,13 @@ export default defineComponent({ label: x.name, data: x.data.slice().reverse(), pointRadius: 0, - tension: 0, borderWidth: 2, borderColor: x.color ? x.color : getColor(i), borderDash: x.borderDash || [], borderJoinStyle: 'round', backgroundColor: alpha(x.color ? x.color : getColor(i), 0.1), + barPercentage: 0.9, + categoryPercentage: 0.9, fill: x.type === 'area', hidden: !!x.hidden, })), @@ -218,6 +224,7 @@ export default defineComponent({ scales: { x: { type: 'time', + stacked: props.stacked, time: { stepSize: 1, unit: props.span === 'day' ? 'month' : 'day', @@ -688,6 +695,21 @@ export default defineComponent({ }; }; + const fetchPerUserDriveChart = async (): Promise => { + const raw = await os.api('charts/user/drive', { userId: props.args.user.id, limit: props.limit, span: props.span }); + return { + series: [{ + name: 'Inc', + type: 'area', + data: format(raw.incSize), + }, { + name: 'Dec', + type: 'area', + data: format(raw.decSize), + }], + }; + }; + const fetchAndRender = async () => { const fetchData = () => { switch (props.src) { @@ -718,6 +740,7 @@ export default defineComponent({ case 'instance-drive-files-total': return fetchInstanceDriveFilesChart(true); case 'per-user-notes': return fetchPerUserNotesChart(); + case 'per-user-drive': return fetchPerUserDriveChart(); } }; fetching.value = true; diff --git a/packages/client/src/pages/settings/drive.vue b/packages/client/src/pages/settings/drive.vue index 134fa63308..9309eb5ec7 100644 --- a/packages/client/src/pages/settings/drive.vue +++ b/packages/client/src/pages/settings/drive.vue @@ -19,7 +19,7 @@ -
+
@@ -45,8 +45,7 @@ import * as os from '@/os'; import bytes from '@/filters/bytes'; import * as symbols from '@/symbols'; import { defaultStore } from '@/store'; - -// TODO: render chart +import MkChart from '@/components/chart.vue'; export default defineComponent({ components: { @@ -55,6 +54,7 @@ export default defineComponent({ FormSection, MkKeyValue, FormSplit, + MkChart, }, emits: ['info'], diff --git a/packages/client/src/pages/user/index.activity.vue b/packages/client/src/pages/user/index.activity.vue index 43a4f476f1..ebb251d4cc 100644 --- a/packages/client/src/pages/user/index.activity.vue +++ b/packages/client/src/pages/user/index.activity.vue @@ -3,7 +3,7 @@
- +
-- cgit v1.2.3-freya From 80fa92fb9723126a40b190ac1a3cf93a62214f62 Mon Sep 17 00:00:00 2001 From: syuilo Date: Sun, 6 Feb 2022 00:43:22 +0900 Subject: improve chart --- locales/ja-JP.yml | 4 +- packages/backend/src/server/api/endpoints/stats.ts | 8 +- packages/client/src/components/chart.vue | 88 ++++++++++------------ packages/client/src/components/instance-stats.vue | 6 +- 4 files changed, 45 insertions(+), 61 deletions(-) (limited to 'packages/client/src') diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 8fd41e533b..1393384626 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1296,8 +1296,8 @@ _exportOrImport: excludeInactiveUsers: "使われていないアカウントを除外" _charts: - federationInstancesIncDec: "連合の増減" - federationInstancesTotal: "連合の合計" + federation: "連合" + apRequest: "リクエスト" usersIncDec: "ユーザーの増減" usersTotal: "ユーザーの合計" activeUsers: "アクティブユーザー数" diff --git a/packages/backend/src/server/api/endpoints/stats.ts b/packages/backend/src/server/api/endpoints/stats.ts index 164ca1c53f..17281888a6 100644 --- a/packages/backend/src/server/api/endpoints/stats.ts +++ b/packages/backend/src/server/api/endpoints/stats.ts @@ -56,8 +56,6 @@ export default define(meta, async () => { reactionsCount, //originalReactionsCount, instances, - driveUsageLocal, - driveUsageRemote, ] = await Promise.all([ Notes.count({ cache: 3600000 }), // 1 hour Notes.count({ where: { userHost: null }, cache: 3600000 }), @@ -66,8 +64,6 @@ export default define(meta, async () => { NoteReactions.count({ cache: 3600000 }), // 1 hour //NoteReactions.count({ where: { userHost: null }, cache: 3600000 }), federationChart.getChart('hour', 1, null).then(chart => chart.instance.total[0]), - driveChart.getChart('hour', 1, null).then(chart => chart.local.totalSize[0] * 1000), - driveChart.getChart('hour', 1, null).then(chart => chart.remote.totalSize[0] * 1000), ]); return { @@ -78,7 +74,7 @@ export default define(meta, async () => { reactionsCount, //originalReactionsCount, instances, - driveUsageLocal, - driveUsageRemote, + driveUsageLocal: 0, + driveUsageRemote: 0, }; }); diff --git a/packages/client/src/components/chart.vue b/packages/client/src/components/chart.vue index ea0df55355..d1b5ad9d1b 100644 --- a/packages/client/src/components/chart.vue +++ b/packages/client/src/components/chart.vue @@ -339,20 +339,51 @@ export default defineComponent({ // TODO }; - const fetchFederationInstancesChart = async (total: boolean): Promise => { + const fetchFederationChart = async (): Promise => { const raw = await os.api('charts/federation', { limit: props.limit, span: props.span }); return { series: [{ - name: 'Instances', + name: 'Instances total', type: 'area', - data: format(total - ? raw.instance.total - : sum(raw.instance.inc, negate(raw.instance.dec)) - ), + data: format(raw.instance.total), + }, { + name: 'Instances inc/dec', + type: 'area', + data: format(sum(raw.instance.inc, negate(raw.instance.dec))), + }, { + name: 'Delivered instances', + type: 'area', + data: format(raw.deliveredInstances), + }, { + name: 'Inbox instances', + type: 'area', + data: format(raw.inboxInstances), }], }; }; + const fetchApRequestChart = async (): Promise => { + const raw = await os.api('charts/ap-request', { limit: props.limit, span: props.span }); + return { + series: [{ + name: 'In', + type: 'area', + color: '#008FFB', + data: format(raw.inboxReceived) + }, { + name: 'Out (succ)', + type: 'area', + color: '#00E396', + data: format(raw.deliverSucceeded) + }, { + name: 'Out (fail)', + type: 'area', + color: '#FEB019', + data: format(raw.deliverFailed) + }] + }; + }; + const fetchNotesChart = async (type: string): Promise => { const raw = await os.api('charts/notes', { limit: props.limit, span: props.span }); return { @@ -491,26 +522,6 @@ export default defineComponent({ }; }; - const fetchDriveTotalChart = async (): Promise => { - const raw = await os.api('charts/drive', { limit: props.limit, span: props.span }); - return { - bytes: true, - series: [{ - name: 'Combined', - type: 'line', - data: format(sum(raw.local.totalSize, raw.remote.totalSize)), - }, { - name: 'Local', - type: 'area', - data: format(raw.local.totalSize), - }, { - name: 'Remote', - type: 'area', - data: format(raw.remote.totalSize), - }], - }; - }; - const fetchDriveFilesChart = async (): Promise => { const raw = await os.api('charts/drive', { limit: props.limit, span: props.span }); return { @@ -546,25 +557,6 @@ export default defineComponent({ }; }; - const fetchDriveFilesTotalChart = async (): Promise => { - const raw = await os.api('charts/drive', { limit: props.limit, span: props.span }); - return { - series: [{ - name: 'Combined', - type: 'line', - data: format(sum(raw.local.totalCount, raw.remote.totalCount)), - }, { - name: 'Local', - type: 'area', - data: format(raw.local.totalCount), - }, { - name: 'Remote', - type: 'area', - data: format(raw.remote.totalCount), - }], - }; - }; - const fetchInstanceRequestsChart = async (): Promise => { const raw = await os.api('charts/instance', { host: props.args.host, limit: props.limit, span: props.span }); return { @@ -713,8 +705,8 @@ export default defineComponent({ const fetchAndRender = async () => { const fetchData = () => { switch (props.src) { - case 'federation-instances': return fetchFederationInstancesChart(false); - case 'federation-instances-total': return fetchFederationInstancesChart(true); + case 'federation': return fetchFederationChart(); + case 'ap-request': return fetchApRequestChart(); case 'users': return fetchUsersChart(false); case 'users-total': return fetchUsersChart(true); case 'active-users': return fetchActiveUsersChart(); @@ -723,9 +715,7 @@ export default defineComponent({ case 'remote-notes': return fetchNotesChart('remote'); case 'notes-total': return fetchNotesTotalChart(); case 'drive': return fetchDriveChart(); - case 'drive-total': return fetchDriveTotalChart(); case 'drive-files': return fetchDriveFilesChart(); - case 'drive-files-total': return fetchDriveFilesTotalChart(); case 'instance-requests': return fetchInstanceRequestsChart(); case 'instance-users': return fetchInstanceUsersChart(false); diff --git a/packages/client/src/components/instance-stats.vue b/packages/client/src/components/instance-stats.vue index 409c3a49ca..d2aa5a151a 100644 --- a/packages/client/src/components/instance-stats.vue +++ b/packages/client/src/components/instance-stats.vue @@ -3,8 +3,8 @@
- - + + @@ -19,9 +19,7 @@ - - -- cgit v1.2.3-freya From cc7a1808ecc6874af9cc96f8ed09e62b3f6b4e39 Mon Sep 17 00:00:00 2001 From: syuilo Date: Sun, 6 Feb 2022 06:24:06 +0900 Subject: imprpve active users chart --- .../backend/migration/1644095659741-chart-v11.js | 91 ++++++++++++++++++++++ packages/backend/src/server/api/define.ts | 1 + .../src/services/chart/charts/active-users.ts | 22 +++++- .../services/chart/charts/entities/active-users.ts | 10 ++- packages/backend/src/services/note/create.ts | 5 +- packages/client/src/components/chart.vue | 36 +++++++-- 6 files changed, 149 insertions(+), 16 deletions(-) create mode 100644 packages/backend/migration/1644095659741-chart-v11.js (limited to 'packages/client/src') diff --git a/packages/backend/migration/1644095659741-chart-v11.js b/packages/backend/migration/1644095659741-chart-v11.js new file mode 100644 index 0000000000..40b1c3072b --- /dev/null +++ b/packages/backend/migration/1644095659741-chart-v11.js @@ -0,0 +1,91 @@ +const { MigrationInterface, QueryRunner } = require("typeorm"); + +module.exports = class chartV111644095659741 { + name = 'chartV111644095659741' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "___local_users"`); + await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "___remote_users"`); + await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "unique_temp___local_users"`); + await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "unique_temp___remote_users"`); + await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "___local_users"`); + await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "___remote_users"`); + await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "unique_temp___local_users"`); + await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "unique_temp___remote_users"`); + await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "unique_temp___users" character varying array NOT NULL DEFAULT '{}'`); + await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "___users" integer NOT NULL DEFAULT '0'`); + await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "unique_temp___notedUsers" character varying array NOT NULL DEFAULT '{}'`); + await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "___notedUsers" smallint NOT NULL DEFAULT '0'`); + await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "unique_temp___registeredWithinWeek" character varying array NOT NULL DEFAULT '{}'`); + await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "___registeredWithinWeek" smallint NOT NULL DEFAULT '0'`); + await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "unique_temp___registeredWithinMonth" character varying array NOT NULL DEFAULT '{}'`); + await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "___registeredWithinMonth" smallint NOT NULL DEFAULT '0'`); + await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "unique_temp___registeredWithinYear" character varying array NOT NULL DEFAULT '{}'`); + await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "___registeredWithinYear" smallint NOT NULL DEFAULT '0'`); + await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "unique_temp___registeredOutsideWeek" character varying array NOT NULL DEFAULT '{}'`); + await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "___registeredOutsideWeek" smallint NOT NULL DEFAULT '0'`); + await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "unique_temp___registeredOutsideMonth" character varying array NOT NULL DEFAULT '{}'`); + await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "___registeredOutsideMonth" smallint NOT NULL DEFAULT '0'`); + await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "unique_temp___registeredOutsideYear" character varying array NOT NULL DEFAULT '{}'`); + await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "___registeredOutsideYear" smallint NOT NULL DEFAULT '0'`); + await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "unique_temp___users" character varying array NOT NULL DEFAULT '{}'`); + await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "___users" integer NOT NULL DEFAULT '0'`); + await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "unique_temp___notedUsers" character varying array NOT NULL DEFAULT '{}'`); + await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "___notedUsers" smallint NOT NULL DEFAULT '0'`); + await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "unique_temp___registeredWithinWeek" character varying array NOT NULL DEFAULT '{}'`); + await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "___registeredWithinWeek" smallint NOT NULL DEFAULT '0'`); + await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "unique_temp___registeredWithinMonth" character varying array NOT NULL DEFAULT '{}'`); + await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "___registeredWithinMonth" smallint NOT NULL DEFAULT '0'`); + await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "unique_temp___registeredWithinYear" character varying array NOT NULL DEFAULT '{}'`); + await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "___registeredWithinYear" smallint NOT NULL DEFAULT '0'`); + await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "unique_temp___registeredOutsideWeek" character varying array NOT NULL DEFAULT '{}'`); + await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "___registeredOutsideWeek" smallint NOT NULL DEFAULT '0'`); + await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "unique_temp___registeredOutsideMonth" character varying array NOT NULL DEFAULT '{}'`); + await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "___registeredOutsideMonth" smallint NOT NULL DEFAULT '0'`); + await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "unique_temp___registeredOutsideYear" character varying array NOT NULL DEFAULT '{}'`); + await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "___registeredOutsideYear" smallint NOT NULL DEFAULT '0'`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "___registeredOutsideYear"`); + await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "unique_temp___registeredOutsideYear"`); + await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "___registeredOutsideMonth"`); + await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "unique_temp___registeredOutsideMonth"`); + await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "___registeredOutsideWeek"`); + await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "unique_temp___registeredOutsideWeek"`); + await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "___registeredWithinYear"`); + await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "unique_temp___registeredWithinYear"`); + await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "___registeredWithinMonth"`); + await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "unique_temp___registeredWithinMonth"`); + await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "___registeredWithinWeek"`); + await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "unique_temp___registeredWithinWeek"`); + await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "___notedUsers"`); + await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "unique_temp___notedUsers"`); + await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "___users"`); + await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "unique_temp___users"`); + await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "___registeredOutsideYear"`); + await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "unique_temp___registeredOutsideYear"`); + await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "___registeredOutsideMonth"`); + await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "unique_temp___registeredOutsideMonth"`); + await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "___registeredOutsideWeek"`); + await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "unique_temp___registeredOutsideWeek"`); + await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "___registeredWithinYear"`); + await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "unique_temp___registeredWithinYear"`); + await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "___registeredWithinMonth"`); + await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "unique_temp___registeredWithinMonth"`); + await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "___registeredWithinWeek"`); + await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "unique_temp___registeredWithinWeek"`); + await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "___notedUsers"`); + await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "unique_temp___notedUsers"`); + await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "___users"`); + await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "unique_temp___users"`); + await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "unique_temp___remote_users" character varying array NOT NULL DEFAULT '{}'`); + await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "unique_temp___local_users" character varying array NOT NULL DEFAULT '{}'`); + await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "___remote_users" integer NOT NULL DEFAULT '0'`); + await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "___local_users" integer NOT NULL DEFAULT '0'`); + await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "unique_temp___remote_users" character varying array NOT NULL DEFAULT '{}'`); + await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "unique_temp___local_users" character varying array NOT NULL DEFAULT '{}'`); + await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "___remote_users" integer NOT NULL DEFAULT '0'`); + await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "___local_users" integer NOT NULL DEFAULT '0'`); + } +} diff --git a/packages/backend/src/server/api/define.ts b/packages/backend/src/server/api/define.ts index 71e5fadde0..e0720e2adb 100644 --- a/packages/backend/src/server/api/define.ts +++ b/packages/backend/src/server/api/define.ts @@ -9,6 +9,7 @@ type NonOptional = T extends undefined ? never : T; type SimpleUserInfo = { id: ILocalUser['id']; + createdAt: ILocalUser['createdAt']; host: ILocalUser['host']; username: ILocalUser['username']; uri: ILocalUser['uri']; diff --git a/packages/backend/src/services/chart/charts/active-users.ts b/packages/backend/src/services/chart/charts/active-users.ts index 9e5c332d3b..7fef99d3a9 100644 --- a/packages/backend/src/services/chart/charts/active-users.ts +++ b/packages/backend/src/services/chart/charts/active-users.ts @@ -4,6 +4,10 @@ import { User } from '@/models/entities/user'; import { Users } from '@/models/index'; import { name, schema } from './entities/active-users'; +const week = 1000 * 60 * 60 * 24 * 7; +const month = 1000 * 60 * 60 * 24 * 30; +const year = 1000 * 60 * 60 * 24 * 365; + /** * アクティブユーザーに関するチャート */ @@ -19,10 +23,22 @@ export default class ActiveUsersChart extends Chart { } @autobind - public async update(user: { id: User['id'], host: User['host'] }): Promise { + public async update(user: { id: User['id'], host: null, createdAt: User['createdAt'] }): Promise { + await this.commit({ + 'users': [user.id], + 'registeredWithinWeek': (Date.now() - user.createdAt.getTime() < week) ? [user.id] : [], + 'registeredWithinMonth': (Date.now() - user.createdAt.getTime() < month) ? [user.id] : [], + 'registeredWithinYear': (Date.now() - user.createdAt.getTime() < year) ? [user.id] : [], + 'registeredOutsideWeek': (Date.now() - user.createdAt.getTime() > week) ? [user.id] : [], + 'registeredOutsideMonth': (Date.now() - user.createdAt.getTime() > month) ? [user.id] : [], + 'registeredOutsideYear': (Date.now() - user.createdAt.getTime() > year) ? [user.id] : [], + }); + } + + @autobind + public async noted(user: { id: User['id'], host: null, createdAt: User['createdAt'] }): Promise { await this.commit({ - 'local.users': Users.isLocalUser(user) ? [user.id] : [], - 'remote.users': Users.isLocalUser(user) ? [] : [user.id], + 'notedUsers': [user.id], }); } } diff --git a/packages/backend/src/services/chart/charts/entities/active-users.ts b/packages/backend/src/services/chart/charts/entities/active-users.ts index 3caa938d35..ee16ef1278 100644 --- a/packages/backend/src/services/chart/charts/entities/active-users.ts +++ b/packages/backend/src/services/chart/charts/entities/active-users.ts @@ -3,8 +3,14 @@ import Chart from '../../core'; export const name = 'activeUsers'; export const schema = { - 'local.users': { uniqueIncrement: true }, - 'remote.users': { uniqueIncrement: true }, + 'users': { uniqueIncrement: true }, + 'notedUsers': { uniqueIncrement: true, range: 'small' }, + 'registeredWithinWeek': { uniqueIncrement: true, range: 'small' }, + 'registeredWithinMonth': { uniqueIncrement: true, range: 'small' }, + 'registeredWithinYear': { uniqueIncrement: true, range: 'small' }, + 'registeredOutsideWeek': { uniqueIncrement: true, range: 'small' }, + 'registeredOutsideMonth': { uniqueIncrement: true, range: 'small' }, + 'registeredOutsideYear': { uniqueIncrement: true, range: 'small' }, } as const; export const entity = Chart.schemaToEntity(name, schema); diff --git a/packages/backend/src/services/note/create.ts b/packages/backend/src/services/note/create.ts index cfb1f8c1e5..53a86fb773 100644 --- a/packages/backend/src/services/note/create.ts +++ b/packages/backend/src/services/note/create.ts @@ -111,7 +111,7 @@ type Option = { app?: App | null; }; -export default async (user: { id: User['id']; username: User['username']; host: User['host']; isSilenced: User['isSilenced']; }, data: Option, silent = false) => new Promise(async (res, rej) => { +export default async (user: { id: User['id']; username: User['username']; host: User['host']; isSilenced: User['isSilenced']; createdAt: User['createdAt']; }, data: Option, silent = false) => new Promise(async (res, rej) => { // チャンネル外にリプライしたら対象のスコープに合わせる // (クライアントサイドでやっても良い処理だと思うけどとりあえずサーバーサイドで) if (data.reply && data.channel && data.reply.channelId !== data.channel.id) { @@ -297,8 +297,7 @@ export default async (user: { id: User['id']; username: User['username']; host: } if (!silent) { - // ローカルユーザーのチャートはタイムライン取得時に更新しているのでリモートユーザーの場合だけでよい - if (Users.isRemoteUser(user)) activeUsersChart.update(user); + if (Users.isLocalUser(user)) activeUsersChart.noted(user); // 未読通知を作成 if (data.visibility === 'specified') { diff --git a/packages/client/src/components/chart.vue b/packages/client/src/components/chart.vue index d1b5ad9d1b..6d6f57188f 100644 --- a/packages/client/src/components/chart.vue +++ b/packages/client/src/components/chart.vue @@ -61,7 +61,7 @@ const alpha = (hex, a) => { return `rgba(${r}, ${g}, ${b}, ${a})`; }; -const colors = ['#008FFB', '#00E396', '#FEB019', '#FF4560']; +const colors = ['#008FFB', '#00E396', '#FEB019', '#FF4560', '#e300db']; const getColor = (i) => { return colors[i % colors.length]; }; @@ -471,17 +471,37 @@ export default defineComponent({ const raw = await os.api('charts/active-users', { limit: props.limit, span: props.span }); return { series: [{ - name: 'Combined', - type: 'line', - data: format(sum(raw.local.users, raw.remote.users)), + name: 'Users', + type: 'area', + data: format(raw.users), }, { - name: 'Local', + name: 'Noted', type: 'area', - data: format(raw.local.users), + data: format(raw.notedUsers), }, { - name: 'Remote', + name: '< Week', + type: 'area', + data: format(raw.registeredWithinWeek), + }, { + name: '< Month', + type: 'area', + data: format(raw.registeredWithinMonth), + }, { + name: '< Year', + type: 'area', + data: format(raw.registeredWithinYear), + }, { + name: '> Week', + type: 'area', + data: format(raw.registeredOutsideWeek), + }, { + name: '> Month', + type: 'area', + data: format(raw.registeredOutsideMonth), + }, { + name: '> Year', type: 'area', - data: format(raw.remote.users), + data: format(raw.registeredOutsideYear), }], }; }; -- cgit v1.2.3-freya From 6cbd66b5344ae37e70cb712b5ebc55547b02074a Mon Sep 17 00:00:00 2001 From: tamaina Date: Sun, 6 Feb 2022 10:59:36 +0900 Subject: fix: v-sizeディレクティブの動作を修正 (#8249) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix size directive behavior not activated * calc * wip * cache computed classes * fix Vue3では使えなくなった * 不要なIntersection Observerを削除 * comment --- packages/client/src/directives/get-size.ts | 65 ++++++++---- packages/client/src/directives/size.ts | 155 ++++++++++++++++++----------- 2 files changed, 140 insertions(+), 80 deletions(-) (limited to 'packages/client/src') diff --git a/packages/client/src/directives/get-size.ts b/packages/client/src/directives/get-size.ts index e3b5dea0f3..1fcd0718dc 100644 --- a/packages/client/src/directives/get-size.ts +++ b/packages/client/src/directives/get-size.ts @@ -1,34 +1,55 @@ import { Directive } from 'vue'; -export default { - mounted(src, binding, vn) { - const calc = () => { - const height = src.clientHeight; - const width = src.clientWidth; - - // 要素が(一時的に)DOMに存在しないときは計算スキップ - if (height === 0) return; - - binding.value(width, height); - }; +const mountings = new Map void; +}>(); + +function calc(src: Element) { + const info = mountings.get(src); + const height = src.clientHeight; + const width = src.clientWidth; + + if (!info) return; + + // アクティベート前などでsrcが描画されていない場合 + if (!height) { + // IntersectionObserverで表示検出する + if (!info.intersection) { + info.intersection = new IntersectionObserver(entries => { + if (entries.some(entry => entry.isIntersecting)) calc(src); + }); + } + info.intersection.observe(src); + return; + } + if (info.intersection) { + info.intersection.disconnect() + delete info.intersection; + }; - calc(); + info.fn(width, height); +}; - // Vue3では使えなくなった - // 無くても大丈夫か...? - // TODO: ↑大丈夫じゃなかったので解決策を探す - //vn.context.$on('hook:activated', calc); +export default { + mounted(src, binding, vn) { - const ro = new ResizeObserver((entries, observer) => { - calc(); + const resize = new ResizeObserver((entries, observer) => { + calc(src); }); - ro.observe(src); + resize.observe(src); - src._get_size_ro_ = ro; + mountings.set(src, { resize, fn: binding.value, }); + calc(src); }, unmounted(src, binding, vn) { binding.value(0, 0); - src._get_size_ro_.unobserve(src); + const info = mountings.get(src); + if (!info) return; + info.resize.disconnect(); + if (info.intersection) info.intersection.disconnect(); + mountings.delete(src); } -} as Directive; +} as Directive void>; diff --git a/packages/client/src/directives/size.ts b/packages/client/src/directives/size.ts index a72a97abcc..36f649f180 100644 --- a/packages/client/src/directives/size.ts +++ b/packages/client/src/directives/size.ts @@ -1,68 +1,107 @@ import { Directive } from 'vue'; +type Value = { max?: number[]; min?: number[]; }; + //const observers = new Map(); +const mountings = new Map(); + +type ClassOrder = { + add: string[]; + remove: string[]; +}; + +const cache = new Map(); + +function getClassOrder(width: number, queue: Value): ClassOrder { + const getMaxClass = (v: number) => `max-width_${v}px`; + const getMinClass = (v: number) => `min-width_${v}px`; + + return { + add: [ + ...(queue.max ? queue.max.filter(v => width <= v).map(getMaxClass) : []), + ...(queue.min ? queue.min.filter(v => width >= v).map(getMinClass) : []), + ], + remove: [ + ...(queue.max ? queue.max.filter(v => width > v).map(getMaxClass) : []), + ...(queue.min ? queue.min.filter(v => width < v).map(getMinClass) : []), + ] + }; +} + +function applyClassOrder(el: Element, order: ClassOrder) { + el.classList.add(...order.add); + el.classList.remove(...order.remove); +} + +function getOrderName(width: number, queue: Value): string { + return `${width}|${queue.max ? queue.max.join(',') : ''}|${queue.min ? queue.min.join(',') : ''}`; +} + +function calc(el: Element) { + const info = mountings.get(el); + const width = el.clientWidth; + + if (!info || info.previousWidth === width) return; + + // アクティベート前などでsrcが描画されていない場合 + if (!width) { + // IntersectionObserverで表示検出する + if (!info.intersection) { + info.intersection = new IntersectionObserver(entries => { + if (entries.some(entry => entry.isIntersecting)) calc(el); + }); + } + info.intersection.observe(el); + return; + } + if (info.intersection) { + info.intersection.disconnect() + delete info.intersection; + }; + + mountings.set(el, Object.assign(info, { previousWidth: width })); + + const cached = cache.get(getOrderName(width, info.value)); + if (cached) { + applyClassOrder(el, cached); + } else { + const order = getClassOrder(width, info.value); + cache.set(getOrderName(width, info.value), order); + applyClassOrder(el, order); + } +} export default { mounted(src, binding, vn) { - const query = binding.value; - - const addClass = (el: Element, cls: string) => { - el.classList.add(cls); - }; - - const removeClass = (el: Element, cls: string) => { - el.classList.remove(cls); - }; - - const calc = () => { - const width = src.clientWidth; - - // 要素が(一時的に)DOMに存在しないときは計算スキップ - if (width === 0) return; - - if (query.max) { - for (const v of query.max) { - if (width <= v) { - addClass(src, 'max-width_' + v + 'px'); - } else { - removeClass(src, 'max-width_' + v + 'px'); - } - } - } - if (query.min) { - for (const v of query.min) { - if (width >= v) { - addClass(src, 'min-width_' + v + 'px'); - } else { - removeClass(src, 'min-width_' + v + 'px'); - } - } - } - }; - - calc(); - - window.addEventListener('resize', calc); - - // Vue3では使えなくなった - // 無くても大丈夫か...? - // TODO: ↑大丈夫じゃなかったので解決策を探す - //vn.context.$on('hook:activated', calc); - - //const ro = new ResizeObserver((entries, observer) => { - // calc(); - //}); - - //ro.observe(el); - - // TODO: 新たにプロパティを作るのをやめMapを使う - // ただメモリ的には↓の方が省メモリかもしれないので検討中 - //el._ro_ = ro; - src._calc_ = calc; + const resize = new ResizeObserver((entries, observer) => { + calc(src); + }); + + mountings.set(src, { + value: binding.value, + resize, + previousWidth: 0, + }); + + calc(src); + resize.observe(src); + }, + + updated(src, binding, vn) { + mountings.set(src, Object.assign({}, mountings.get(src), { value: binding.value })); + calc(src); }, unmounted(src, binding, vn) { - //el._ro_.unobserve(el); - window.removeEventListener('resize', src._calc_); + const info = mountings.get(src); + if (!info) return; + info.resize.disconnect(); + if (info.intersection) info.intersection.disconnect(); + mountings.delete(src); } -} as Directive; +} as Directive; -- cgit v1.2.3-freya From 3ff89fa7ecb643abf944ce966ffbf7f49000ca3f Mon Sep 17 00:00:00 2001 From: nullobsi Date: Sat, 5 Feb 2022 23:02:48 -0800 Subject: feat: Option to show replies in timeline (rebase #7685) (#8202) * Add an option for timeline replies. Credit to Emilis (puffaboo) * update db on request --- locales/ja-JP.yml | 2 ++ .../backend/migration/1629833361000-AddShowTLReplies.js | 15 +++++++++++++++ packages/backend/src/models/entities/user.ts | 6 ++++++ packages/backend/src/models/repositories/user.ts | 1 + packages/backend/src/remote/activitypub/models/person.ts | 1 + .../src/server/api/common/generate-replies-query.ts | 4 ++-- packages/backend/src/server/api/endpoints/i/update.ts | 5 +++++ .../src/server/api/stream/channels/global-timeline.ts | 2 +- .../src/server/api/stream/channels/home-timeline.ts | 2 +- .../src/server/api/stream/channels/hybrid-timeline.ts | 2 +- .../src/server/api/stream/channels/local-timeline.ts | 2 +- packages/client/src/pages/settings/profile.vue | 4 +++- 12 files changed, 39 insertions(+), 7 deletions(-) create mode 100644 packages/backend/migration/1629833361000-AddShowTLReplies.js (limited to 'packages/client/src') diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 1393384626..ee23f2c5cd 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -141,6 +141,8 @@ flagAsBot: "Botとして設定" flagAsBotDescription: "このアカウントがプログラムによって運用される場合は、このフラグをオンにします。オンにすると、反応の連鎖を防ぐためのフラグとして他の開発者に役立ったり、Misskeyのシステム上での扱いがBotに合ったものになります。" flagAsCat: "Catとして設定" flagAsCatDescription: "このアカウントが猫であることを示す場合は、このフラグをオンにします。" +flagShowTimelineReplies: "タイムラインにノートへの返信を表示する" +flagShowTimelineRepliesDescription: "オンにすると、タイムラインにユーザーのノート以外にもそのユーザーの他のノートへの返信を表示します。" autoAcceptFollowed: "フォロー中ユーザーからのフォロリクを自動承認" addAccount: "アカウントを追加" loginFailed: "ログインに失敗しました" diff --git a/packages/backend/migration/1629833361000-AddShowTLReplies.js b/packages/backend/migration/1629833361000-AddShowTLReplies.js new file mode 100644 index 0000000000..bfd4ab7ff7 --- /dev/null +++ b/packages/backend/migration/1629833361000-AddShowTLReplies.js @@ -0,0 +1,15 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +class addShowTLReplies1629833361000 { + constructor() { + this.name = 'addShowTLReplies1629833361000'; + } + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "user" ADD "showTimelineReplies" boolean NOT NULL DEFAULT false`); + await queryRunner.query(`COMMENT ON COLUMN "user"."showTimelineReplies" IS 'Whether to show users replying to other users in the timeline.'`); + } + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "showTimelineReplies"`); + } +} +exports.addShowTLReplies1629833361000 = addShowTLReplies1629833361000; diff --git a/packages/backend/src/models/entities/user.ts b/packages/backend/src/models/entities/user.ts index 0aa01ba00a..e4d9a3ced9 100644 --- a/packages/backend/src/models/entities/user.ts +++ b/packages/backend/src/models/entities/user.ts @@ -225,6 +225,12 @@ export class User { }) public followersUri: string | null; + @Column('boolean', { + default: false, + comment: 'Whether to show users replying to other users in the timeline' + }) + public showTimelineReplies: boolean; + @Index({ unique: true }) @Column('char', { length: 16, nullable: true, unique: true, diff --git a/packages/backend/src/models/repositories/user.ts b/packages/backend/src/models/repositories/user.ts index 2b8398832d..33b2b32fee 100644 --- a/packages/backend/src/models/repositories/user.ts +++ b/packages/backend/src/models/repositories/user.ts @@ -220,6 +220,7 @@ export class UserRepository extends Repository { isModerator: user.isModerator || falsy, isBot: user.isBot || falsy, isCat: user.isCat || falsy, + showTimelineReplies: user.showTimelineReplies || falsy, instance: user.host ? Instances.findOne({ host: user.host }).then(instance => instance ? { name: instance.name, softwareName: instance.softwareName, diff --git a/packages/backend/src/remote/activitypub/models/person.ts b/packages/backend/src/remote/activitypub/models/person.ts index 19a7a70903..aaccf51fa9 100644 --- a/packages/backend/src/remote/activitypub/models/person.ts +++ b/packages/backend/src/remote/activitypub/models/person.ts @@ -164,6 +164,7 @@ export async function createPerson(uri: string, resolver?: Resolver): Promise, me?: { id: User['id'] } | null) { +export function generateRepliesQuery(q: SelectQueryBuilder, me?: Pick | null) { if (me == null) { q.andWhere(new Brackets(qb => { qb .where(`note.replyId IS NULL`) // 返信ではない @@ -10,7 +10,7 @@ export function generateRepliesQuery(q: SelectQueryBuilder, me?: { id: User .andWhere('note.replyUserId = note.userId'); })); })); - } else { + } else if(!me.showTimelineReplies) { q.andWhere(new Brackets(qb => { qb .where(`note.replyId IS NULL`) // 返信ではない .orWhere('note.replyUserId = :meId', { meId: me.id }) // 返信だけど自分のノートへの返信 diff --git a/packages/backend/src/server/api/endpoints/i/update.ts b/packages/backend/src/server/api/endpoints/i/update.ts index 6b7e53aa1f..eb57aa2bfc 100644 --- a/packages/backend/src/server/api/endpoints/i/update.ts +++ b/packages/backend/src/server/api/endpoints/i/update.ts @@ -96,6 +96,10 @@ export const meta = { validator: $.optional.bool, }, + showTimelineReplies: { + validator: $.optional.bool, + }, + injectFeaturedNote: { validator: $.optional.bool, }, @@ -197,6 +201,7 @@ export default define(meta, async (ps, _user, token) => { if (typeof ps.hideOnlineStatus === 'boolean') updates.hideOnlineStatus = ps.hideOnlineStatus; if (typeof ps.publicReactions === 'boolean') profileUpdates.publicReactions = ps.publicReactions; if (typeof ps.isBot === 'boolean') updates.isBot = ps.isBot; + if (typeof ps.showTimelineReplies === 'boolean') updates.showTimelineReplies = ps.showTimelineReplies; if (typeof ps.carefulBot === 'boolean') profileUpdates.carefulBot = ps.carefulBot; if (typeof ps.autoAcceptFollowed === 'boolean') profileUpdates.autoAcceptFollowed = ps.autoAcceptFollowed; if (typeof ps.noCrawle === 'boolean') profileUpdates.noCrawle = ps.noCrawle; diff --git a/packages/backend/src/server/api/stream/channels/global-timeline.ts b/packages/backend/src/server/api/stream/channels/global-timeline.ts index f14f597aac..ecd87d093d 100644 --- a/packages/backend/src/server/api/stream/channels/global-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/global-timeline.ts @@ -43,7 +43,7 @@ export default class extends Channel { } // 関係ない返信は除外 - if (note.reply) { + if (note.reply && !this.user!.showTimelineReplies) { const reply = note.reply; // 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合 if (reply.userId !== this.user!.id && note.userId !== this.user!.id && reply.userId !== note.userId) return; diff --git a/packages/backend/src/server/api/stream/channels/home-timeline.ts b/packages/backend/src/server/api/stream/channels/home-timeline.ts index 3bd491421d..445db5c382 100644 --- a/packages/backend/src/server/api/stream/channels/home-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/home-timeline.ts @@ -54,7 +54,7 @@ export default class extends Channel { } // 関係ない返信は除外 - if (note.reply) { + if (note.reply && !this.user!.showTimelineReplies) { const reply = note.reply; // 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合 if (reply.userId !== this.user!.id && note.userId !== this.user!.id && reply.userId !== note.userId) return; diff --git a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts index 0ae19aa7ce..c0be71fe2d 100644 --- a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts @@ -62,7 +62,7 @@ export default class extends Channel { if (isInstanceMuted(note, new Set(this.userProfile?.mutedInstances ?? []))) return; // 関係ない返信は除外 - if (note.reply) { + if (note.reply && !this.user!.showTimelineReplies) { const reply = note.reply; // 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合 if (reply.userId !== this.user!.id && note.userId !== this.user!.id && reply.userId !== note.userId) return; diff --git a/packages/backend/src/server/api/stream/channels/local-timeline.ts b/packages/backend/src/server/api/stream/channels/local-timeline.ts index 3178b1d511..ae8f62ba61 100644 --- a/packages/backend/src/server/api/stream/channels/local-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/local-timeline.ts @@ -43,7 +43,7 @@ export default class extends Channel { } // 関係ない返信は除外 - if (note.reply) { + if (note.reply && !this.user!.showTimelineReplies) { const reply = note.reply; // 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合 if (reply.userId !== this.user!.id && note.userId !== this.user!.id && reply.userId !== note.userId) return; diff --git a/packages/client/src/pages/settings/profile.vue b/packages/client/src/pages/settings/profile.vue index 66b654d87f..9b30d1c8e0 100644 --- a/packages/client/src/pages/settings/profile.vue +++ b/packages/client/src/pages/settings/profile.vue @@ -38,7 +38,7 @@ {{ i18n.ts.flagAsCat }} - + {{ i18n.ts.flagShowTimelineReplies }} {{ i18n.ts.flagAsBot }} {{ i18n.ts.alwaysMarkSensitive }} @@ -68,6 +68,7 @@ const profile = reactive({ lang: $i.lang, isBot: $i.isBot, isCat: $i.isCat, + showTimelineReplies: $i.showTimelineReplies, alwaysMarkNsfw: $i.alwaysMarkNsfw, }); @@ -97,6 +98,7 @@ function save() { lang: profile.lang || null, isBot: !!profile.isBot, isCat: !!profile.isCat, + showTimelineReplies: !!profile.showTimelineReplies, alwaysMarkNsfw: !!profile.alwaysMarkNsfw, }); } -- cgit v1.2.3-freya From e5d6d1dcf0ae21c50f237a677bf54d09273670f4 Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 7 Feb 2022 14:56:49 +0900 Subject: :art: --- packages/client/src/components/chart.vue | 1 + 1 file changed, 1 insertion(+) (limited to 'packages/client/src') diff --git a/packages/client/src/components/chart.vue b/packages/client/src/components/chart.vue index 6d6f57188f..c78a61751d 100644 --- a/packages/client/src/components/chart.vue +++ b/packages/client/src/components/chart.vue @@ -235,6 +235,7 @@ export default defineComponent({ }, ticks: { display: props.detailed, + maxRotation: 0, }, adapters: { date: { -- cgit v1.2.3-freya From 5792eea1b1868da90e9a782fb3f308d4396b3e94 Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 8 Feb 2022 15:37:31 +0900 Subject: enhance(client): improve tooltip position calclation --- packages/client/src/components/chart-tooltip.vue | 2 +- packages/client/src/components/ui/tooltip.vue | 141 ++++++++++++++++++++--- 2 files changed, 126 insertions(+), 17 deletions(-) (limited to 'packages/client/src') diff --git a/packages/client/src/components/chart-tooltip.vue b/packages/client/src/components/chart-tooltip.vue index b080eaf2b4..20e094a5a7 100644 --- a/packages/client/src/components/chart-tooltip.vue +++ b/packages/client/src/components/chart-tooltip.vue @@ -1,5 +1,5 @@ + + + + + + + + {{ $ts.showFixedPostForm }} @@ -127,6 +135,7 @@ export default defineComponent({ }, computed: { + overridedDeviceKind: defaultStore.makeGetterSetter('overridedDeviceKind'), serverDisconnectedBehavior: defaultStore.makeGetterSetter('serverDisconnectedBehavior'), reduceAnimation: defaultStore.makeGetterSetter('animation', v => !v, v => !v), useBlurEffectForModal: defaultStore.makeGetterSetter('useBlurEffectForModal'), @@ -193,6 +202,10 @@ export default defineComponent({ instanceTicker() { this.reloadAsk(); }, + + overridedDeviceKind() { + this.reloadAsk(); + }, }, methods: { diff --git a/packages/client/src/scripts/device-kind.ts b/packages/client/src/scripts/device-kind.ts new file mode 100644 index 0000000000..544cac0604 --- /dev/null +++ b/packages/client/src/scripts/device-kind.ts @@ -0,0 +1,10 @@ +import { defaultStore } from '@/store'; + +const ua = navigator.userAgent.toLowerCase(); +const isTablet = /ipad/.test(ua) || (/mobile|iphone|android/.test(ua) && window.innerWidth > 700); +const isSmartphone = !isTablet && /mobile|iphone|android/.test(ua); + +export const deviceKind = defaultStore.state.overridedDeviceKind ? defaultStore.state.overridedDeviceKind + : isSmartphone ? 'smartphone' + : isTablet ? 'tablet' + : 'desktop'; diff --git a/packages/client/src/scripts/is-mobile.ts b/packages/client/src/scripts/is-mobile.ts deleted file mode 100644 index 60cb59f91e..0000000000 --- a/packages/client/src/scripts/is-mobile.ts +++ /dev/null @@ -1,2 +0,0 @@ -const ua = navigator.userAgent.toLowerCase(); -export const isMobile = /mobile|iphone|ipad|android/.test(ua); diff --git a/packages/client/src/store.ts b/packages/client/src/store.ts index b80fc8bbe3..0e71060cda 100644 --- a/packages/client/src/store.ts +++ b/packages/client/src/store.ts @@ -106,6 +106,10 @@ export const defaultStore = markRaw(new Storage('base', { } }, + overridedDeviceKind: { + where: 'device', + default: null as null | 'smartphone' | 'tablet' | 'desktop', + }, serverDisconnectedBehavior: { where: 'device', default: 'quiet' as 'quiet' | 'reload' | 'dialog' -- cgit v1.2.3-freya From aa7ab1c1e00cd0cba6d01106a658aa01d1ab8561 Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 8 Feb 2022 19:20:09 +0900 Subject: :art: --- packages/client/src/components/chart.vue | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'packages/client/src') diff --git a/packages/client/src/components/chart.vue b/packages/client/src/components/chart.vue index c78a61751d..a273cc9467 100644 --- a/packages/client/src/components/chart.vue +++ b/packages/client/src/components/chart.vue @@ -199,6 +199,7 @@ export default defineComponent({ parsing: false, label: x.name, data: x.data.slice().reverse(), + tension: 0.3, pointRadius: 0, borderWidth: 2, borderColor: x.color ? x.color : getColor(i), @@ -208,6 +209,7 @@ export default defineComponent({ barPercentage: 0.9, categoryPercentage: 0.9, fill: x.type === 'area', + clip: 8, hidden: !!x.hidden, })), }, @@ -216,7 +218,7 @@ export default defineComponent({ layout: { padding: { left: 0, - right: 0, + right: 8, top: 0, bottom: 0, }, -- cgit v1.2.3-freya From e9190599ca10442f8272a4f0eac8c37bf5315d66 Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 8 Feb 2022 22:36:07 +0900 Subject: :art: --- packages/client/package.json | 1 + packages/client/src/components/chart.vue | 14 ++++++++++++++ packages/client/yarn.lock | 5 +++++ 3 files changed, 20 insertions(+) (limited to 'packages/client/src') diff --git a/packages/client/package.json b/packages/client/package.json index 355de401e9..7bc74a1c17 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -45,6 +45,7 @@ "broadcast-channel": "4.9.0", "chart.js": "3.7.0", "chartjs-adapter-date-fns": "2.0.0", + "chartjs-plugin-gradient": "0.2.1", "chartjs-plugin-zoom": "1.2.0", "compare-versions": "4.1.3", "content-disposition": "0.5.4", diff --git a/packages/client/src/components/chart.vue b/packages/client/src/components/chart.vue index a273cc9467..a0a4755d12 100644 --- a/packages/client/src/components/chart.vue +++ b/packages/client/src/components/chart.vue @@ -29,6 +29,7 @@ import { import 'chartjs-adapter-date-fns'; import { enUS } from 'date-fns/locale'; import zoomPlugin from 'chartjs-plugin-zoom'; +import gradient from 'chartjs-plugin-gradient'; import * as os from '@/os'; import { defaultStore } from '@/store'; import MkChartTooltip from '@/components/chart-tooltip.vue'; @@ -49,6 +50,7 @@ Chart.register( SubTitle, Filler, zoomPlugin, + gradient, ); const sum = (...arr) => arr.reduce((r, a) => r.map((b, i) => a[i] + b)); @@ -191,6 +193,8 @@ export default defineComponent({ // フォントカラー Chart.defaults.color = getComputedStyle(document.documentElement).getPropertyValue('--fg'); + const maxes = data.series.map((x, i) => Math.max(...x.data.map(d => d.y))); + chartInstance = new Chart(chartEl.value, { type: props.bar ? 'bar' : 'line', data: { @@ -206,6 +210,15 @@ export default defineComponent({ borderDash: x.borderDash || [], borderJoinStyle: 'round', backgroundColor: alpha(x.color ? x.color : getColor(i), 0.1), + gradient: { + backgroundColor: { + axis: 'y', + colors: { + 0: alpha(x.color ? x.color : getColor(i), 0), + [maxes[i]]: alpha(x.color ? x.color : getColor(i), 0.1), + }, + }, + }, barPercentage: 0.9, categoryPercentage: 0.9, fill: x.type === 'area', @@ -312,6 +325,7 @@ export default defineComponent({ }, } }, + gradient, }, }, plugins: [{ diff --git a/packages/client/yarn.lock b/packages/client/yarn.lock index b1013c5789..5670241442 100644 --- a/packages/client/yarn.lock +++ b/packages/client/yarn.lock @@ -1579,6 +1579,11 @@ chartjs-adapter-date-fns@2.0.0: resolved "https://registry.yarnpkg.com/chartjs-adapter-date-fns/-/chartjs-adapter-date-fns-2.0.0.tgz#5e53b2f660b993698f936f509c86dddf9ed44c6b" integrity sha512-rmZINGLe+9IiiEB0kb57vH3UugAtYw33anRiw5kS2Tu87agpetDDoouquycWc9pRsKtQo5j+vLsYHyr8etAvFw== +chartjs-plugin-gradient@0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/chartjs-plugin-gradient/-/chartjs-plugin-gradient-0.2.1.tgz#9d6d4f1a04a8d2ffca769adb068df4d0678b8f8f" + integrity sha512-hcNQ+B0LuiK9QXhbEc0tUtW3s0a8lOBUJViOCw2xHbnNCIp3Pul/tQHR1aIjMo3HiHu4nOb7NKqFd4NTUEsi4Q== + chartjs-plugin-zoom@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/chartjs-plugin-zoom/-/chartjs-plugin-zoom-1.2.0.tgz#dad0861b2d171bca1f6d11b3e3e917bc12b950ff" -- cgit v1.2.3-freya From adf3493af855cc23e3e4b82c2fa462e05c4a26c1 Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 8 Feb 2022 23:12:37 +0900 Subject: feat: notes/instance/perUserNotesチャートに添付ファイル付きノートの数を追加 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 3 ++- .../backend/migration/1644328606241-chart-v12.js | 27 ++++++++++++++++++++++ .../src/services/chart/charts/entities/instance.ts | 1 + .../src/services/chart/charts/entities/notes.ts | 2 ++ .../chart/charts/entities/per-user-notes.ts | 1 + .../backend/src/services/chart/charts/instance.ts | 1 + .../backend/src/services/chart/charts/notes.ts | 1 + .../src/services/chart/charts/per-user-notes.ts | 1 + packages/client/src/components/chart.vue | 25 ++++++++++++++++++-- 9 files changed, 59 insertions(+), 3 deletions(-) create mode 100644 packages/backend/migration/1644328606241-chart-v12.js (limited to 'packages/client/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 225de735ac..8746328a5e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,11 +14,12 @@ You should also include the user name that made the change. ### Note このリリースはマイグレーションの規模が大きいため、インスタンスによってはマイグレーションに時間がかかる可能性があります。 -マイグレーションが終わらない場合は、チャートの情報はリセットされてしまいますが`___chart___`で始まるテーブルの**レコード**を全て削除(テーブル自体は消さないでください)してから再度試す方法もあります。 +マイグレーションが終わらない場合は、チャートの情報はリセットされてしまいますが`__chart__`で始まるテーブルの**レコード**を全て削除(テーブル自体は消さないでください)してから再度試す方法もあります。 ### Improvements - チャートエンジンの強化 @syuilo - テーブルサイズの削減 + - notes/instance/perUserNotesチャートに添付ファイル付きノートの数を追加 - activeUsersチャートに新しい項目を追加 - federationチャートに新しい項目を追加 - apRequestチャートを追加 diff --git a/packages/backend/migration/1644328606241-chart-v12.js b/packages/backend/migration/1644328606241-chart-v12.js new file mode 100644 index 0000000000..226de6ece9 --- /dev/null +++ b/packages/backend/migration/1644328606241-chart-v12.js @@ -0,0 +1,27 @@ +const { MigrationInterface, QueryRunner } = require("typeorm"); + +module.exports = class chartV121644328606241 { + name = 'chartV121644328606241' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "__chart__notes" ADD "___local_diffs_withFile" integer NOT NULL DEFAULT '0'`); + await queryRunner.query(`ALTER TABLE "__chart__notes" ADD "___remote_diffs_withFile" integer NOT NULL DEFAULT '0'`); + await queryRunner.query(`ALTER TABLE "__chart_day__notes" ADD "___local_diffs_withFile" integer NOT NULL DEFAULT '0'`); + await queryRunner.query(`ALTER TABLE "__chart_day__notes" ADD "___remote_diffs_withFile" integer NOT NULL DEFAULT '0'`); + await queryRunner.query(`ALTER TABLE "__chart__instance" ADD "___notes_diffs_withFile" integer NOT NULL DEFAULT '0'`); + await queryRunner.query(`ALTER TABLE "__chart_day__instance" ADD "___notes_diffs_withFile" integer NOT NULL DEFAULT '0'`); + await queryRunner.query(`ALTER TABLE "__chart__per_user_notes" ADD "___diffs_withFile" smallint NOT NULL DEFAULT '0'`); + await queryRunner.query(`ALTER TABLE "__chart_day__per_user_notes" ADD "___diffs_withFile" smallint NOT NULL DEFAULT '0'`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "__chart_day__per_user_notes" DROP COLUMN "___diffs_withFile"`); + await queryRunner.query(`ALTER TABLE "__chart__per_user_notes" DROP COLUMN "___diffs_withFile"`); + await queryRunner.query(`ALTER TABLE "__chart_day__instance" DROP COLUMN "___notes_diffs_withFile"`); + await queryRunner.query(`ALTER TABLE "__chart__instance" DROP COLUMN "___notes_diffs_withFile"`); + await queryRunner.query(`ALTER TABLE "__chart_day__notes" DROP COLUMN "___remote_diffs_withFile"`); + await queryRunner.query(`ALTER TABLE "__chart_day__notes" DROP COLUMN "___local_diffs_withFile"`); + await queryRunner.query(`ALTER TABLE "__chart__notes" DROP COLUMN "___remote_diffs_withFile"`); + await queryRunner.query(`ALTER TABLE "__chart__notes" DROP COLUMN "___local_diffs_withFile"`); + } +} diff --git a/packages/backend/src/services/chart/charts/entities/instance.ts b/packages/backend/src/services/chart/charts/entities/instance.ts index 0ff9e7b6b8..b98e1640c8 100644 --- a/packages/backend/src/services/chart/charts/entities/instance.ts +++ b/packages/backend/src/services/chart/charts/entities/instance.ts @@ -12,6 +12,7 @@ export const schema = { 'notes.diffs.normal': {}, 'notes.diffs.reply': {}, 'notes.diffs.renote': {}, + 'notes.diffs.withFile': {}, 'users.total': { accumulate: true }, 'users.inc': { range: 'small' }, 'users.dec': { range: 'small' }, diff --git a/packages/backend/src/services/chart/charts/entities/notes.ts b/packages/backend/src/services/chart/charts/entities/notes.ts index 296328f102..f9b9b20eed 100644 --- a/packages/backend/src/services/chart/charts/entities/notes.ts +++ b/packages/backend/src/services/chart/charts/entities/notes.ts @@ -9,12 +9,14 @@ export const schema = { 'local.diffs.normal': {}, 'local.diffs.reply': {}, 'local.diffs.renote': {}, + 'local.diffs.withFile': {}, 'remote.total': { accumulate: true }, 'remote.inc': {}, 'remote.dec': {}, 'remote.diffs.normal': {}, 'remote.diffs.reply': {}, 'remote.diffs.renote': {}, + 'remote.diffs.withFile': {}, } as const; export const entity = Chart.schemaToEntity(name, schema); diff --git a/packages/backend/src/services/chart/charts/entities/per-user-notes.ts b/packages/backend/src/services/chart/charts/entities/per-user-notes.ts index bf7e4422be..562cde9b00 100644 --- a/packages/backend/src/services/chart/charts/entities/per-user-notes.ts +++ b/packages/backend/src/services/chart/charts/entities/per-user-notes.ts @@ -9,6 +9,7 @@ export const schema = { 'diffs.normal': { range: 'small' }, 'diffs.reply': { range: 'small' }, 'diffs.renote': { range: 'small' }, + 'diffs.withFile': { range: 'small' }, } as const; export const entity = Chart.schemaToEntity(name, schema, true); diff --git a/packages/backend/src/services/chart/charts/instance.ts b/packages/backend/src/services/chart/charts/instance.ts index 4f953cd690..5ea4d567e1 100644 --- a/packages/backend/src/services/chart/charts/instance.ts +++ b/packages/backend/src/services/chart/charts/instance.ts @@ -74,6 +74,7 @@ export default class InstanceChart extends Chart { 'notes.diffs.normal': note.replyId == null && note.renoteId == null ? (isAdditional ? 1 : -1) : 0, 'notes.diffs.renote': note.renoteId != null ? (isAdditional ? 1 : -1) : 0, 'notes.diffs.reply': note.replyId != null ? (isAdditional ? 1 : -1) : 0, + 'notes.diffs.withFile': note.fileIds.length > 0 ? (isAdditional ? 1 : -1) : 0, }, toPuny(host)); } diff --git a/packages/backend/src/services/chart/charts/notes.ts b/packages/backend/src/services/chart/charts/notes.ts index 4bbfa6704f..5c56a9a718 100644 --- a/packages/backend/src/services/chart/charts/notes.ts +++ b/packages/backend/src/services/chart/charts/notes.ts @@ -38,6 +38,7 @@ export default class NotesChart extends Chart { [`${prefix}.diffs.normal`]: note.replyId == null && note.renoteId == null ? (isAdditional ? 1 : -1) : 0, [`${prefix}.diffs.renote`]: note.renoteId != null ? (isAdditional ? 1 : -1) : 0, [`${prefix}.diffs.reply`]: note.replyId != null ? (isAdditional ? 1 : -1) : 0, + [`${prefix}.diffs.withFile`]: note.fileIds.length > 0 ? (isAdditional ? 1 : -1) : 0, }); } } diff --git a/packages/backend/src/services/chart/charts/per-user-notes.ts b/packages/backend/src/services/chart/charts/per-user-notes.ts index ead353139b..6a4f0363b2 100644 --- a/packages/backend/src/services/chart/charts/per-user-notes.ts +++ b/packages/backend/src/services/chart/charts/per-user-notes.ts @@ -34,6 +34,7 @@ export default class PerUserNotesChart extends Chart { 'diffs.normal': note.replyId == null && note.renoteId == null ? (isAdditional ? 1 : -1) : 0, 'diffs.renote': note.renoteId != null ? (isAdditional ? 1 : -1) : 0, 'diffs.reply': note.replyId != null ? (isAdditional ? 1 : -1) : 0, + 'diffs.withFile': note.fileIds.length > 0 ? (isAdditional ? 1 : -1) : 0, }, user.id); } } diff --git a/packages/client/src/components/chart.vue b/packages/client/src/components/chart.vue index a0a4755d12..5e96429e80 100644 --- a/packages/client/src/components/chart.vue +++ b/packages/client/src/components/chart.vue @@ -63,9 +63,16 @@ const alpha = (hex, a) => { return `rgba(${r}, ${g}, ${b}, ${a})`; }; -const colors = ['#008FFB', '#00E396', '#FEB019', '#FF4560', '#e300db']; +const colors = { + blue: '#008FFB', + green: '#00E396', + yellow: '#FEB019', + red: '#FF4560', + purple: '#e300db', +}; +const colorSets = [colors.blue, colors.green, colors.yellow, colors.red, colors.purple]; const getColor = (i) => { - return colors[i % colors.length]; + return colorSets[i % colorSets.length]; }; export default defineComponent({ @@ -251,6 +258,7 @@ export default defineComponent({ ticks: { display: props.detailed, maxRotation: 0, + autoSkipPadding: 16, }, adapters: { date: { @@ -268,6 +276,7 @@ export default defineComponent({ }, ticks: { display: props.detailed, + //mirror: true, }, }, }, @@ -412,6 +421,7 @@ export default defineComponent({ ? sum(raw.local.inc, negate(raw.local.dec), raw.remote.inc, negate(raw.remote.dec)) : sum(raw[type].inc, negate(raw[type].dec)) ), + color: '#888888', }, { name: 'Renotes', type: 'area', @@ -419,6 +429,7 @@ export default defineComponent({ ? sum(raw.local.diffs.renote, raw.remote.diffs.renote) : raw[type].diffs.renote ), + color: colors.green, }, { name: 'Replies', type: 'area', @@ -426,6 +437,7 @@ export default defineComponent({ ? sum(raw.local.diffs.reply, raw.remote.diffs.reply) : raw[type].diffs.reply ), + color: colors.yellow, }, { name: 'Normal', type: 'area', @@ -433,6 +445,15 @@ export default defineComponent({ ? sum(raw.local.diffs.normal, raw.remote.diffs.normal) : raw[type].diffs.normal ), + color: colors.blue, + }, { + name: 'With file', + type: 'area', + data: format(type == 'combined' + ? sum(raw.local.diffs.withFile, raw.remote.diffs.withFile) + : raw[type].diffs.withFile + ), + color: colors.purple, }], }; }; -- cgit v1.2.3-freya From f50dd5d677cacf0ebc24857e20263c223b190627 Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 8 Feb 2022 23:15:43 +0900 Subject: :art: --- packages/client/src/components/chart.vue | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'packages/client/src') diff --git a/packages/client/src/components/chart.vue b/packages/client/src/components/chart.vue index 5e96429e80..2b63b5c3e3 100644 --- a/packages/client/src/components/chart.vue +++ b/packages/client/src/components/chart.vue @@ -512,34 +512,42 @@ export default defineComponent({ name: 'Users', type: 'area', data: format(raw.users), + color: '#888888', }, { name: 'Noted', type: 'area', data: format(raw.notedUsers), + color: colors.blue, }, { name: '< Week', type: 'area', data: format(raw.registeredWithinWeek), + color: colors.green, }, { name: '< Month', type: 'area', data: format(raw.registeredWithinMonth), + color: colors.yellow, }, { name: '< Year', type: 'area', data: format(raw.registeredWithinYear), + color: colors.red, }, { name: '> Week', type: 'area', data: format(raw.registeredOutsideWeek), + color: colors.yellow, }, { name: '> Month', type: 'area', data: format(raw.registeredOutsideMonth), + color: colors.red, }, { name: '> Year', type: 'area', data: format(raw.registeredOutsideYear), + color: colors.purple, }], }; }; -- cgit v1.2.3-freya From f7ff39e7ff32e2b73f855159b4c2000129cb17fb Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 8 Feb 2022 23:25:48 +0900 Subject: chore: better label --- packages/client/src/components/chart.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'packages/client/src') diff --git a/packages/client/src/components/chart.vue b/packages/client/src/components/chart.vue index 2b63b5c3e3..da7d378bd9 100644 --- a/packages/client/src/components/chart.vue +++ b/packages/client/src/components/chart.vue @@ -509,7 +509,7 @@ export default defineComponent({ const raw = await os.api('charts/active-users', { limit: props.limit, span: props.span }); return { series: [{ - name: 'Users', + name: 'Active', type: 'area', data: format(raw.users), color: '#888888', -- cgit v1.2.3-freya From f4e28983a1355caf842825ec71037b956b7b98ac Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 8 Feb 2022 23:43:51 +0900 Subject: feat: improve federation chart --- packages/backend/migration/1644331238153-chart-v13.js | 19 +++++++++++++++++++ packages/backend/src/queue/processors/deliver.ts | 3 ++- .../src/services/chart/charts/entities/federation.ts | 1 + .../backend/src/services/chart/charts/federation.ts | 6 ++++-- packages/client/src/components/chart.vue | 13 +++++++++++-- 5 files changed, 37 insertions(+), 5 deletions(-) create mode 100644 packages/backend/migration/1644331238153-chart-v13.js (limited to 'packages/client/src') diff --git a/packages/backend/migration/1644331238153-chart-v13.js b/packages/backend/migration/1644331238153-chart-v13.js new file mode 100644 index 0000000000..ed36659b34 --- /dev/null +++ b/packages/backend/migration/1644331238153-chart-v13.js @@ -0,0 +1,19 @@ +const { MigrationInterface, QueryRunner } = require("typeorm"); + +module.exports = class chartV131644331238153 { + name = 'chartV131644331238153' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "__chart__federation" ADD "unique_temp___stalled" character varying array NOT NULL DEFAULT '{}'`); + await queryRunner.query(`ALTER TABLE "__chart__federation" ADD "___stalled" smallint NOT NULL DEFAULT '0'`); + await queryRunner.query(`ALTER TABLE "__chart_day__federation" ADD "unique_temp___stalled" character varying array NOT NULL DEFAULT '{}'`); + await queryRunner.query(`ALTER TABLE "__chart_day__federation" ADD "___stalled" smallint NOT NULL DEFAULT '0'`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "__chart_day__federation" DROP COLUMN "___stalled"`); + await queryRunner.query(`ALTER TABLE "__chart_day__federation" DROP COLUMN "unique_temp___stalled"`); + await queryRunner.query(`ALTER TABLE "__chart__federation" DROP COLUMN "___stalled"`); + await queryRunner.query(`ALTER TABLE "__chart__federation" DROP COLUMN "unique_temp___stalled"`); + } +} diff --git a/packages/backend/src/queue/processors/deliver.ts b/packages/backend/src/queue/processors/deliver.ts index e29f991307..bd91dfc3b5 100644 --- a/packages/backend/src/queue/processors/deliver.ts +++ b/packages/backend/src/queue/processors/deliver.ts @@ -62,7 +62,7 @@ export default async (job: Bull.Job) => { instanceChart.requestSent(i.host, true); apRequestChart.deliverSucc(); - federationChart.deliverd(i.host); + federationChart.deliverd(i.host, true); }); return 'Success'; @@ -77,6 +77,7 @@ export default async (job: Bull.Job) => { instanceChart.requestSent(i.host, false); apRequestChart.deliverFail(); + federationChart.deliverd(i.host, false); }); if (res instanceof StatusError) { diff --git a/packages/backend/src/services/chart/charts/entities/federation.ts b/packages/backend/src/services/chart/charts/entities/federation.ts index 5a76c4918c..0c8c20991d 100644 --- a/packages/backend/src/services/chart/charts/entities/federation.ts +++ b/packages/backend/src/services/chart/charts/entities/federation.ts @@ -8,6 +8,7 @@ export const schema = { 'instance.dec': { range: 'small' }, 'deliveredInstances': { uniqueIncrement: true, range: 'small' }, 'inboxInstances': { uniqueIncrement: true, range: 'small' }, + 'stalled': { uniqueIncrement: true, range: 'small' }, } as const; export const entity = Chart.schemaToEntity(name, schema); diff --git a/packages/backend/src/services/chart/charts/federation.ts b/packages/backend/src/services/chart/charts/federation.ts index 3aa448e66f..19c75c98ad 100644 --- a/packages/backend/src/services/chart/charts/federation.ts +++ b/packages/backend/src/services/chart/charts/federation.ts @@ -33,9 +33,11 @@ export default class FederationChart extends Chart { } @autobind - public async deliverd(host: string): Promise { - await this.commit({ + public async deliverd(host: string, succeeded: boolean): Promise { + await this.commit(succeeded ? { 'deliveredInstances': [host], + } : { + 'stalled': [host], }); } diff --git a/packages/client/src/components/chart.vue b/packages/client/src/components/chart.vue index da7d378bd9..f297db7e13 100644 --- a/packages/client/src/components/chart.vue +++ b/packages/client/src/components/chart.vue @@ -372,18 +372,27 @@ export default defineComponent({ name: 'Instances total', type: 'area', data: format(raw.instance.total), + color: '#888888', }, { name: 'Instances inc/dec', type: 'area', data: format(sum(raw.instance.inc, negate(raw.instance.dec))), + color: colors.purple, + }, { + name: 'Inbox instances', + type: 'area', + data: format(raw.inboxInstances), + color: colors.blue, }, { name: 'Delivered instances', type: 'area', data: format(raw.deliveredInstances), + color: colors.green, }, { - name: 'Inbox instances', + name: 'Stalled instances', type: 'area', - data: format(raw.inboxInstances), + data: format(raw.stalled), + color: colors.red, }], }; }; -- cgit v1.2.3-freya From eb894c330fa3c247e43437c163696ac778fa7b64 Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 8 Feb 2022 23:53:01 +0900 Subject: fix(client): 環境に依っては返信する際のカーソル位置が正しくない問題を修正 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 3 ++- packages/client/src/components/post-form.vue | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'packages/client/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 92835014ec..1c1d2ce01d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,8 +32,9 @@ You should also include the user name that made the change. - Client: タイムライン種別を切り替えると「新しいノートがあります」の表示が残留してしまうのを修正 @tamaina - Client: UIのサイズがおかしくなる問題の修正 @tamaina - Client: Setting instance information of notes to always show breaks the timeline @Johann150 +- Client: 環境に依っては返信する際のカーソル位置が正しくない問題を修正 @syuilo - API: Fix API cast @mei23 -- チャートの定期resyncが動作していない問題を修正 +- チャートの定期resyncが動作していない問題を修正 @syuilo ## 12.103.1 (2022/02/02) diff --git a/packages/client/src/components/post-form.vue b/packages/client/src/components/post-form.vue index 535218ecf9..64a6478f45 100644 --- a/packages/client/src/components/post-form.vue +++ b/packages/client/src/components/post-form.vue @@ -342,6 +342,7 @@ function addTag(tag: string) { function focus() { textareaEl.focus(); + textareaEl.setSelectionRange(textareaEl.value.length, textareaEl.value.length); } function chooseFileFrom(ev) { -- cgit v1.2.3-freya From 7fcd9435f32f051e5068c21933f4209e214e0281 Mon Sep 17 00:00:00 2001 From: syuilo Date: Wed, 9 Feb 2022 03:46:58 +0900 Subject: feat: introduce intersection calculation of charts --- .../backend/migration/1644344266289-chart-v14.js | 47 ++++++++++++++++++++++ .../src/server/api/endpoints/channels/timeline.ts | 2 +- .../server/api/endpoints/notes/global-timeline.ts | 2 +- .../server/api/endpoints/notes/hybrid-timeline.ts | 2 +- .../server/api/endpoints/notes/local-timeline.ts | 2 +- .../src/server/api/endpoints/notes/timeline.ts | 2 +- .../api/endpoints/notes/user-list-timeline.ts | 2 +- .../src/services/chart/charts/active-users.ts | 8 ++-- .../services/chart/charts/entities/active-users.ts | 5 ++- .../chart/charts/entities/test-intersection.ts | 11 +++++ .../src/services/chart/charts/test-intersection.ts | 32 +++++++++++++++ packages/backend/src/services/chart/core.ts | 29 +++++++++++++ packages/backend/src/services/note/create.ts | 2 +- packages/backend/test/chart.ts | 44 ++++++++++++++++++++ packages/client/src/components/chart.vue | 16 +++++--- 15 files changed, 188 insertions(+), 18 deletions(-) create mode 100644 packages/backend/migration/1644344266289-chart-v14.js create mode 100644 packages/backend/src/services/chart/charts/entities/test-intersection.ts create mode 100644 packages/backend/src/services/chart/charts/test-intersection.ts (limited to 'packages/client/src') diff --git a/packages/backend/migration/1644344266289-chart-v14.js b/packages/backend/migration/1644344266289-chart-v14.js new file mode 100644 index 0000000000..8496cc2d42 --- /dev/null +++ b/packages/backend/migration/1644344266289-chart-v14.js @@ -0,0 +1,47 @@ +const { MigrationInterface, QueryRunner } = require("typeorm"); + +module.exports = class chartV141644344266289 { + name = 'chartV141644344266289' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "unique_temp___users"`); + await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "___users"`); + await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "unique_temp___notedUsers"`); + await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "___notedUsers"`); + await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "unique_temp___users"`); + await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "___users"`); + await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "unique_temp___notedUsers"`); + await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "___notedUsers"`); + await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "___readWrite" smallint NOT NULL DEFAULT '0'`); + await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "unique_temp___read" character varying array NOT NULL DEFAULT '{}'`); + await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "___read" smallint NOT NULL DEFAULT '0'`); + await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "unique_temp___write" character varying array NOT NULL DEFAULT '{}'`); + await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "___write" smallint NOT NULL DEFAULT '0'`); + await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "___readWrite" smallint NOT NULL DEFAULT '0'`); + await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "unique_temp___read" character varying array NOT NULL DEFAULT '{}'`); + await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "___read" smallint NOT NULL DEFAULT '0'`); + await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "unique_temp___write" character varying array NOT NULL DEFAULT '{}'`); + await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "___write" smallint NOT NULL DEFAULT '0'`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "___write"`); + await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "unique_temp___write"`); + await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "___read"`); + await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "unique_temp___read"`); + await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "___readWrite"`); + await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "___write"`); + await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "unique_temp___write"`); + await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "___read"`); + await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "unique_temp___read"`); + await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "___readWrite"`); + await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "___notedUsers" smallint NOT NULL DEFAULT '0'`); + await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "unique_temp___notedUsers" character varying array NOT NULL DEFAULT '{}'`); + await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "___users" integer NOT NULL DEFAULT '0'`); + await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "unique_temp___users" character varying array NOT NULL DEFAULT '{}'`); + await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "___notedUsers" smallint NOT NULL DEFAULT '0'`); + await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "unique_temp___notedUsers" character varying array NOT NULL DEFAULT '{}'`); + await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "___users" integer NOT NULL DEFAULT '0'`); + await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "unique_temp___users" character varying array NOT NULL DEFAULT '{}'`); + } +} diff --git a/packages/backend/src/server/api/endpoints/channels/timeline.ts b/packages/backend/src/server/api/endpoints/channels/timeline.ts index 927ce7c741..2639095f8a 100644 --- a/packages/backend/src/server/api/endpoints/channels/timeline.ts +++ b/packages/backend/src/server/api/endpoints/channels/timeline.ts @@ -80,7 +80,7 @@ export default define(meta, async (ps, user) => { const timeline = await query.take(ps.limit!).getMany(); - if (user) activeUsersChart.update(user); + if (user) activeUsersChart.read(user); return await Notes.packMany(timeline, user); }); diff --git a/packages/backend/src/server/api/endpoints/notes/global-timeline.ts b/packages/backend/src/server/api/endpoints/notes/global-timeline.ts index cac8b7d8a9..cdd110994e 100644 --- a/packages/backend/src/server/api/endpoints/notes/global-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/global-timeline.ts @@ -96,7 +96,7 @@ export default define(meta, async (ps, user) => { process.nextTick(() => { if (user) { - activeUsersChart.update(user); + activeUsersChart.read(user); } }); diff --git a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts index 9683df4611..b438491026 100644 --- a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts @@ -153,7 +153,7 @@ export default define(meta, async (ps, user) => { process.nextTick(() => { if (user) { - activeUsersChart.update(user); + activeUsersChart.read(user); } }); diff --git a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts index 7776644124..ce0bcbeb7b 100644 --- a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts @@ -122,7 +122,7 @@ export default define(meta, async (ps, user) => { process.nextTick(() => { if (user) { - activeUsersChart.update(user); + activeUsersChart.read(user); } }); diff --git a/packages/backend/src/server/api/endpoints/notes/timeline.ts b/packages/backend/src/server/api/endpoints/notes/timeline.ts index 8be2861aec..f8cd083249 100644 --- a/packages/backend/src/server/api/endpoints/notes/timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/timeline.ts @@ -145,7 +145,7 @@ export default define(meta, async (ps, user) => { process.nextTick(() => { if (user) { - activeUsersChart.update(user); + activeUsersChart.read(user); } }); diff --git a/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts b/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts index 89de73fb9d..3512fb3638 100644 --- a/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts @@ -142,7 +142,7 @@ export default define(meta, async (ps, user) => { const timeline = await query.take(ps.limit!).getMany(); - activeUsersChart.update(user); + activeUsersChart.read(user); return await Notes.packMany(timeline, user); }); diff --git a/packages/backend/src/services/chart/charts/active-users.ts b/packages/backend/src/services/chart/charts/active-users.ts index 7fef99d3a9..87dd95f4dc 100644 --- a/packages/backend/src/services/chart/charts/active-users.ts +++ b/packages/backend/src/services/chart/charts/active-users.ts @@ -23,9 +23,9 @@ export default class ActiveUsersChart extends Chart { } @autobind - public async update(user: { id: User['id'], host: null, createdAt: User['createdAt'] }): Promise { + public async read(user: { id: User['id'], host: null, createdAt: User['createdAt'] }): Promise { await this.commit({ - 'users': [user.id], + 'read': [user.id], 'registeredWithinWeek': (Date.now() - user.createdAt.getTime() < week) ? [user.id] : [], 'registeredWithinMonth': (Date.now() - user.createdAt.getTime() < month) ? [user.id] : [], 'registeredWithinYear': (Date.now() - user.createdAt.getTime() < year) ? [user.id] : [], @@ -36,9 +36,9 @@ export default class ActiveUsersChart extends Chart { } @autobind - public async noted(user: { id: User['id'], host: null, createdAt: User['createdAt'] }): Promise { + public async write(user: { id: User['id'], host: null, createdAt: User['createdAt'] }): Promise { await this.commit({ - 'notedUsers': [user.id], + 'write': [user.id], }); } } diff --git a/packages/backend/src/services/chart/charts/entities/active-users.ts b/packages/backend/src/services/chart/charts/entities/active-users.ts index ee16ef1278..843843836d 100644 --- a/packages/backend/src/services/chart/charts/entities/active-users.ts +++ b/packages/backend/src/services/chart/charts/entities/active-users.ts @@ -3,8 +3,9 @@ import Chart from '../../core'; export const name = 'activeUsers'; export const schema = { - 'users': { uniqueIncrement: true }, - 'notedUsers': { uniqueIncrement: true, range: 'small' }, + 'readWrite': { intersection: ['read', 'write'], range: 'small' }, + 'read': { uniqueIncrement: true, range: 'small' }, + 'write': { uniqueIncrement: true, range: 'small' }, 'registeredWithinWeek': { uniqueIncrement: true, range: 'small' }, 'registeredWithinMonth': { uniqueIncrement: true, range: 'small' }, 'registeredWithinYear': { uniqueIncrement: true, range: 'small' }, diff --git a/packages/backend/src/services/chart/charts/entities/test-intersection.ts b/packages/backend/src/services/chart/charts/entities/test-intersection.ts new file mode 100644 index 0000000000..dc56eb93f5 --- /dev/null +++ b/packages/backend/src/services/chart/charts/entities/test-intersection.ts @@ -0,0 +1,11 @@ +import Chart from '../../core'; + +export const name = 'testIntersection'; + +export const schema = { + 'a': { uniqueIncrement: true }, + 'b': { uniqueIncrement: true }, + 'aAndB': { intersection: ['a', 'b'] }, +} as const; + +export const entity = Chart.schemaToEntity(name, schema); diff --git a/packages/backend/src/services/chart/charts/test-intersection.ts b/packages/backend/src/services/chart/charts/test-intersection.ts new file mode 100644 index 0000000000..c6ba71a956 --- /dev/null +++ b/packages/backend/src/services/chart/charts/test-intersection.ts @@ -0,0 +1,32 @@ +import autobind from 'autobind-decorator'; +import Chart, { KVs } from '../core'; +import { name, schema } from './entities/test-intersection'; + +/** + * For testing + */ +// eslint-disable-next-line import/no-default-export +export default class TestIntersectionChart extends Chart { + constructor() { + super(name, schema); + } + + @autobind + protected async queryCurrentState(): Promise>> { + return {}; + } + + @autobind + public async addA(key: string): Promise { + await this.commit({ + a: [key], + }); + } + + @autobind + public async addB(key: string): Promise { + await this.commit({ + b: [key], + }); + } +} diff --git a/packages/backend/src/services/chart/core.ts b/packages/backend/src/services/chart/core.ts index b6db598cfb..7032a09d39 100644 --- a/packages/backend/src/services/chart/core.ts +++ b/packages/backend/src/services/chart/core.ts @@ -46,6 +46,8 @@ const removeDuplicates = (array: any[]) => Array.from(new Set(array)); type Schema = Record; + range?: 'big' | 'small' | 'medium'; // previousな値を引き継ぐかどうか @@ -384,6 +386,33 @@ export default abstract class Chart { } } + // compute intersection + // TODO: intersectionに指定されたカラムがintersectionだった場合の対応 + for (const [k, v] of Object.entries(this.schema)) { + const intersection = v.intersection; + if (intersection) { + const name = columnPrefix + k.replaceAll('.', columnDot); + const firstKey = intersection[0]; + const firstTempColumnName = uniqueTempColumnPrefix + firstKey.replaceAll('.', columnDot); + const currentValuesForHour = new Set([...(finalDiffs[firstKey] ?? []), ...logHour[firstTempColumnName]]); + const currentValuesForDay = new Set([...(finalDiffs[firstKey] ?? []), ...logDay[firstTempColumnName]]); + for (let i = 1; i < intersection.length; i++) { + const targetKey = intersection[i]; + const targetTempColumnName = uniqueTempColumnPrefix + targetKey.replaceAll('.', columnDot); + const targetValuesForHour = new Set([...(finalDiffs[targetKey] ?? []), ...logHour[targetTempColumnName]]); + const targetValuesForDay = new Set([...(finalDiffs[targetKey] ?? []), ...logDay[targetTempColumnName]]); + currentValuesForHour.forEach(v => { + if (!targetValuesForHour.has(v)) currentValuesForHour.delete(v); + }); + currentValuesForDay.forEach(v => { + if (!targetValuesForDay.has(v)) currentValuesForDay.delete(v); + }); + } + queryForHour[name] = currentValuesForHour.size; + queryForDay[name] = currentValuesForDay.size; + } + } + // ログ更新 await Promise.all([ this.repositoryForHour.createQueryBuilder() diff --git a/packages/backend/src/services/note/create.ts b/packages/backend/src/services/note/create.ts index 53a86fb773..7a4c2cef12 100644 --- a/packages/backend/src/services/note/create.ts +++ b/packages/backend/src/services/note/create.ts @@ -297,7 +297,7 @@ export default async (user: { id: User['id']; username: User['username']; host: } if (!silent) { - if (Users.isLocalUser(user)) activeUsersChart.noted(user); + if (Users.isLocalUser(user)) activeUsersChart.write(user); // 未読通知を作成 if (data.visibility === 'specified') { diff --git a/packages/backend/test/chart.ts b/packages/backend/test/chart.ts index 66000bc928..a869e0bae7 100644 --- a/packages/backend/test/chart.ts +++ b/packages/backend/test/chart.ts @@ -6,14 +6,17 @@ import { async, initTestDb } from './utils'; import TestChart from '../src/services/chart/charts/test'; import TestGroupedChart from '../src/services/chart/charts/test-grouped'; import TestUniqueChart from '../src/services/chart/charts/test-unique'; +import TestIntersectionChart from '../src/services/chart/charts/test-intersection'; import * as _TestChart from '../src/services/chart/charts/entities/test'; import * as _TestGroupedChart from '../src/services/chart/charts/entities/test-grouped'; import * as _TestUniqueChart from '../src/services/chart/charts/entities/test-unique'; +import * as _TestIntersectionChart from '../src/services/chart/charts/entities/test-intersection'; describe('Chart', () => { let testChart: TestChart; let testGroupedChart: TestGroupedChart; let testUniqueChart: TestUniqueChart; + let testIntersectionChart: TestIntersectionChart; let clock: lolex.Clock; beforeEach(async(async () => { @@ -21,11 +24,13 @@ describe('Chart', () => { _TestChart.entity.hour, _TestChart.entity.day, _TestGroupedChart.entity.hour, _TestGroupedChart.entity.day, _TestUniqueChart.entity.hour, _TestUniqueChart.entity.day, + _TestIntersectionChart.entity.hour, _TestIntersectionChart.entity.day, ]); testChart = new TestChart(); testGroupedChart = new TestGroupedChart(); testUniqueChart = new TestUniqueChart(); + testIntersectionChart = new TestIntersectionChart(); clock = lolex.install({ now: new Date(Date.UTC(2000, 0, 1, 0, 0, 0)) @@ -426,6 +431,45 @@ describe('Chart', () => { foo: [2, 0, 0], }); })); + + describe('Intersection', () => { + it('条件が満たされていない場合はカウントされない', async(async () => { + await testIntersectionChart.addA('alice'); + await testIntersectionChart.addA('bob'); + await testIntersectionChart.addB('carol'); + await testIntersectionChart.save(); + + const chartHours = await testUniqueChart.getChart('hour', 3, null); + const chartDays = await testUniqueChart.getChart('day', 3, null); + + assert.deepStrictEqual(chartHours, { + aAndB: [0, 0, 0], + }); + + assert.deepStrictEqual(chartDays, { + aAndB: [0, 0, 0], + }); + })); + + it('条件が満たされている場合にカウントされる', async(async () => { + await testIntersectionChart.addA('alice'); + await testIntersectionChart.addA('bob'); + await testIntersectionChart.addB('carol'); + await testIntersectionChart.addB('alice'); + await testIntersectionChart.save(); + + const chartHours = await testUniqueChart.getChart('hour', 3, null); + const chartDays = await testUniqueChart.getChart('day', 3, null); + + assert.deepStrictEqual(chartHours, { + aAndB: [1, 0, 0], + }); + + assert.deepStrictEqual(chartDays, { + aAndB: [1, 0, 0], + }); + })); + }); }); describe('Resync', () => { diff --git a/packages/client/src/components/chart.vue b/packages/client/src/components/chart.vue index f297db7e13..1384789f8d 100644 --- a/packages/client/src/components/chart.vue +++ b/packages/client/src/components/chart.vue @@ -69,6 +69,7 @@ const colors = { yellow: '#FEB019', red: '#FF4560', purple: '#e300db', + orange: '#fe6919', }; const colorSets = [colors.blue, colors.green, colors.yellow, colors.red, colors.purple]; const getColor = (i) => { @@ -518,15 +519,20 @@ export default defineComponent({ const raw = await os.api('charts/active-users', { limit: props.limit, span: props.span }); return { series: [{ - name: 'Active', + name: 'Read & Write', type: 'area', - data: format(raw.users), - color: '#888888', + data: format(raw.readWrite), + color: colors.orange, }, { - name: 'Noted', + name: 'Write', type: 'area', - data: format(raw.notedUsers), + data: format(raw.write), color: colors.blue, + }, { + name: 'Read', + type: 'area', + data: format(raw.read), + color: '#888888', }, { name: '< Week', type: 'area', -- cgit v1.2.3-freya From 744c4c7acb63826e965067b96b86da2b2f1f0dde Mon Sep 17 00:00:00 2001 From: Johann150 Date: Wed, 9 Feb 2022 04:43:59 +0100 Subject: fix: save followers/following visibility (#8276) --- packages/client/src/pages/settings/privacy.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'packages/client/src') diff --git a/packages/client/src/pages/settings/privacy.vue b/packages/client/src/pages/settings/privacy.vue index cfae7e9ca8..a84d2f8786 100644 --- a/packages/client/src/pages/settings/privacy.vue +++ b/packages/client/src/pages/settings/privacy.vue @@ -8,7 +8,7 @@ - + -- cgit v1.2.3-freya From ff59984ff4a3e3adc235eefc8829e9e5d9111c6c Mon Sep 17 00:00:00 2001 From: syuilo Date: Wed, 9 Feb 2022 13:25:08 +0900 Subject: chore: tweak chart labels --- packages/client/src/components/chart.vue | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'packages/client/src') diff --git a/packages/client/src/components/chart.vue b/packages/client/src/components/chart.vue index 1384789f8d..ced0d481c4 100644 --- a/packages/client/src/components/chart.vue +++ b/packages/client/src/components/chart.vue @@ -370,27 +370,27 @@ export default defineComponent({ const raw = await os.api('charts/federation', { limit: props.limit, span: props.span }); return { series: [{ - name: 'Instances total', + name: 'Total', type: 'area', data: format(raw.instance.total), color: '#888888', }, { - name: 'Instances inc/dec', + name: 'Inc/Dec', type: 'area', data: format(sum(raw.instance.inc, negate(raw.instance.dec))), color: colors.purple, }, { - name: 'Inbox instances', + name: 'Received', type: 'area', data: format(raw.inboxInstances), color: colors.blue, }, { - name: 'Delivered instances', + name: 'Delivered', type: 'area', data: format(raw.deliveredInstances), color: colors.green, }, { - name: 'Stalled instances', + name: 'Stalled', type: 'area', data: format(raw.stalled), color: colors.red, -- cgit v1.2.3-freya From 5c5d3c1a240f2b8175d773f084916e38ec2a61bb Mon Sep 17 00:00:00 2001 From: syuilo Date: Wed, 9 Feb 2022 13:38:54 +0900 Subject: fix(client): コントロールパネルのユーザー、ファイルにて、インスタンスの表示範囲切り替えが機能しない問題を修正 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix #8252 --- CHANGELOG.md | 1 + packages/client/src/pages/admin/files.vue | 129 ++++++++++------------- packages/client/src/pages/admin/users.vue | 166 ++++++++++++++---------------- 3 files changed, 132 insertions(+), 164 deletions(-) (limited to 'packages/client/src') diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b15c3a5ac..63b569233b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,7 @@ You should also include the user name that made the change. - Client: UIのサイズがおかしくなる問題の修正 @tamaina - Client: Setting instance information of notes to always show breaks the timeline @Johann150 - Client: 環境に依っては返信する際のカーソル位置が正しくない問題を修正 @syuilo +- Client: コントロールパネルのユーザー、ファイルにて、インスタンスの表示範囲切り替えが機能しない問題を修正 @syuilo - Client: Follows/Followers Visibility changes won't be saved unless clicking on an other checkbox @Johann150 - API: Fix API cast @mei23 - チャートの定期resyncが動作していない問題を修正 @syuilo diff --git a/packages/client/src/pages/admin/files.vue b/packages/client/src/pages/admin/files.vue index 87dd12f489..c62f053092 100644 --- a/packages/client/src/pages/admin/files.vue +++ b/packages/client/src/pages/admin/files.vue @@ -28,7 +28,7 @@
- +