fix: フォロー・フォロワーの公開状況によってはフォロー関連のチャートを表示できないように

This commit is contained in:
kakkokari-gtyih 2024-10-24 20:25:42 +09:00
parent 15ae1605ec
commit 8bacd4aa27
4 changed files with 74 additions and 15 deletions

View file

@ -3,19 +3,29 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { Injectable } from '@nestjs/common';
import { Injectable, Inject } from '@nestjs/common';
import { DI } from '@/di-symbols.js';
import type { UserProfilesRepository } from '@/models/_.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js';
import { RoleService } from '@/core/RoleService.js';
import { Endpoint } from '@/server/api/endpoint-base.js';
import { getJsonSchema } from '@/core/chart/core.js';
import PerUserFollowingChart from '@/core/chart/charts/per-user-following.js';
import { schema } from '@/core/chart/charts/entities/per-user-following.js';
import { ApiError } from '@/server/api/error.js';
export const meta = {
tags: ['charts', 'users', 'following'],
res: getJsonSchema(schema),
allowGet: true,
cacheSec: 60 * 60,
errors: {
ffIsMarkedAsPrivate: {
message: 'This user\'s followings and/or followers is marked as private.',
code: 'FF_IS_MARKED_AS_PRIVATE',
id: 'f9c54d7f-d4c2-4d3c-9a8g-a70daac86512',
},
},
} as const;
export const paramDef = {
@ -32,10 +42,51 @@ export const paramDef = {
@Injectable()
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
constructor(
@Inject(DI.userProfilesRepository)
private userProfilesRepository: UserProfilesRepository,
private roleService: RoleService,
private userEntityService: UserEntityService,
private perUserFollowingChart: PerUserFollowingChart,
) {
super(meta, paramDef, async (ps, me) => {
return await this.perUserFollowingChart.getChart(ps.span, ps.limit, ps.offset ? new Date(ps.offset) : null, ps.userId);
const done = async () => {
return await this.perUserFollowingChart.getChart(ps.span, ps.limit, ps.offset ? new Date(ps.offset) : null, ps.userId);
};
const profile = await this.userProfilesRepository.findOneByOrFail({ userId: ps.userId });
if (profile.followingVisibility === 'public' && profile.followersVisibility === 'public') {
done();
}
const iAmModerator = await this.roleService.isModerator(me);
if (iAmModerator) {
done();
}
if (
(profile.followingVisibility === 'private' || profile.followersVisibility === 'private') &&
(me != null && profile.userId === me.id)
) {
done();
}
if (
me != null && (
(profile.followingVisibility === 'followers' && profile.followersVisibility === 'followers') ||
(profile.followingVisibility === 'followers' && profile.followersVisibility === 'public') ||
(profile.followingVisibility === 'public' && profile.followersVisibility === 'followers')
)
) {
const relations = await this.userEntityService.getRelation(me.id, ps.userId);
if (relations.following) {
done();
}
}
throw new ApiError(meta.errors.ffIsMarkedAsPrivate);
});
}
}

View file

@ -53,7 +53,7 @@ export type ChartSrc =
import { onMounted, ref, shallowRef, watch } from 'vue';
import { Chart } from 'chart.js';
import * as Misskey from 'misskey-js';
import { misskeyApiGet } from '@/scripts/misskey-api.js';
import { misskeyApiGet, misskeyApi } from '@/scripts/misskey-api.js';
import { defaultStore } from '@/store.js';
import { useChartTooltip } from '@/scripts/use-chart-tooltip.js';
import { chartVLine } from '@/scripts/chart-vline.js';
@ -758,8 +758,10 @@ const fetchPerUserPvChart = async (): Promise<typeof chartData> => {
};
const fetchPerUserFollowingChart = async (): Promise<typeof chartData> => {
const raw = await misskeyApiGet('charts/user/following', { userId: props.args?.user?.id, limit: props.limit, span: props.span });
return {
const raw = await misskeyApi('charts/user/following', { userId: props.args?.user?.id!, limit: props.limit, span: props.span }).catch(() => {
return null;
});
return raw != null ? {
series: [{
name: 'Local',
type: 'area',
@ -769,12 +771,14 @@ const fetchPerUserFollowingChart = async (): Promise<typeof chartData> => {
type: 'area',
data: format(raw.remote.followings.total),
}],
};
} : raw;
};
const fetchPerUserFollowersChart = async (): Promise<typeof chartData> => {
const raw = await misskeyApiGet('charts/user/following', { userId: props.args?.user?.id, limit: props.limit, span: props.span });
return {
const raw = await misskeyApi('charts/user/following', { userId: props.args?.user?.id!, limit: props.limit, span: props.span }).catch(() => {
return null;
});
return raw != null ? {
series: [{
name: 'Local',
type: 'area',
@ -784,7 +788,7 @@ const fetchPerUserFollowersChart = async (): Promise<typeof chartData> => {
type: 'area',
data: format(raw.remote.followers.total),
}],
};
} : raw;
};
const fetchPerUserDriveChart = async (): Promise<typeof chartData> => {

View file

@ -32,7 +32,7 @@ const props = defineProps<{
user: Misskey.entities.User;
}>();
const chartEl = shallowRef<HTMLCanvasElement>(null);
const chartEl = shallowRef<HTMLCanvasElement>();
const legendEl = shallowRef<InstanceType<typeof MkChartLegend>>();
const now = new Date();
let chartInstance: Chart = null;
@ -88,7 +88,7 @@ async function renderChart() {
}, extra);
}
chartInstance = new Chart(chartEl.value, {
chartInstance = new Chart(chartEl.value!, {
type: 'bar',
data: {
datasets: [

View file

@ -14,7 +14,10 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #header><i class="ti ti-pencil"></i> Notes</template>
<XNotes :user="user"/>
</MkFoldableSection>
<MkFoldableSection class="item">
<MkFoldableSection
v-if="isFollowersVisibleForMe(user) && isFollowingVisibleForMe(user)"
class="item"
>
<template #header><i class="ti ti-users"></i> Following</template>
<XFollowing :user="user"/>
</MkFoldableSection>
@ -33,9 +36,10 @@ import XNotes from './activity.notes.vue';
import XFollowing from './activity.following.vue';
import MkFoldableSection from '@/components/MkFoldableSection.vue';
import MkHeatmap from '@/components/MkHeatmap.vue';
import { isFollowersVisibleForMe, isFollowingVisibleForMe } from '@/scripts/isFfVisibleForMe.js';
const props = defineProps<{
user: Misskey.entities.User;
user: Misskey.entities.UserDetailed;
}>();
</script>