enhance: 人気ユーザーの算出基準を選べるように
This commit is contained in:
parent
285c4b5d37
commit
d98df9edf1
|
@ -4984,6 +4984,18 @@ export interface Locale extends ILocale {
|
||||||
* お問い合わせ
|
* お問い合わせ
|
||||||
*/
|
*/
|
||||||
"inquiry": string;
|
"inquiry": string;
|
||||||
|
/**
|
||||||
|
* ページ閲覧数
|
||||||
|
*/
|
||||||
|
"pageViewCount": string;
|
||||||
|
/**
|
||||||
|
* 人気のユーザーの算出基準
|
||||||
|
*/
|
||||||
|
"preferPopularUserFactor": string;
|
||||||
|
/**
|
||||||
|
* ページ閲覧数はローカルユーザーにのみ適用されます(リモートユーザーはフォロワー数で表示されます)。「無効」に設定すると、ローカル・リモートどちらの「人気のユーザー」セクションも表示されなくなります。
|
||||||
|
*/
|
||||||
|
"preferPopularUserFactorDescription": string;
|
||||||
"_delivery": {
|
"_delivery": {
|
||||||
/**
|
/**
|
||||||
* 配信状態
|
* 配信状態
|
||||||
|
|
|
@ -1242,6 +1242,9 @@ keepOriginalFilenameDescription: "この設定をオフにすると、アップ
|
||||||
noDescription: "説明文はありません"
|
noDescription: "説明文はありません"
|
||||||
alwaysConfirmFollow: "フォローの際常に確認する"
|
alwaysConfirmFollow: "フォローの際常に確認する"
|
||||||
inquiry: "お問い合わせ"
|
inquiry: "お問い合わせ"
|
||||||
|
pageViewCount: "ページ閲覧数"
|
||||||
|
preferPopularUserFactor: "人気のユーザーの算出基準"
|
||||||
|
preferPopularUserFactorDescription: "ページ閲覧数はローカルユーザーにのみ適用されます(リモートユーザーはフォロワー数で表示されます)。「無効」に設定すると、ローカル・リモートどちらの「人気のユーザー」セクションも表示されなくなります。"
|
||||||
|
|
||||||
_delivery:
|
_delivery:
|
||||||
status: "配信状態"
|
status: "配信状態"
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
export class PreferPopularUserFactor1720879227657 {
|
||||||
|
name = 'PreferPopularUserFactor1720879227657'
|
||||||
|
|
||||||
|
async up(queryRunner) {
|
||||||
|
await queryRunner.query(`CREATE TYPE "meta_preferPopularUserFactor_enum" AS ENUM('follower', 'pv', 'none')`)
|
||||||
|
await queryRunner.query(`ALTER TABLE "meta" ADD "preferPopularUserFactor" "meta_preferPopularUserFactor_enum" NOT NULL DEFAULT 'follower'`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async down(queryRunner) {
|
||||||
|
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "preferPopularUserFactor"`);
|
||||||
|
await queryRunner.query(`DROP TYPE "meta_preferPopularUserFactor_enum"`);
|
||||||
|
}
|
||||||
|
}
|
|
@ -128,6 +128,8 @@ export class MetaEntityService {
|
||||||
|
|
||||||
mediaProxy: this.config.mediaProxy,
|
mediaProxy: this.config.mediaProxy,
|
||||||
enableUrlPreview: instance.urlPreviewEnabled,
|
enableUrlPreview: instance.urlPreviewEnabled,
|
||||||
|
|
||||||
|
preferPopularUserFactor: instance.preferPopularUserFactor,
|
||||||
};
|
};
|
||||||
|
|
||||||
return packed;
|
return packed;
|
||||||
|
|
|
@ -620,4 +620,10 @@ export class MiMeta {
|
||||||
nullable: true,
|
nullable: true,
|
||||||
})
|
})
|
||||||
public urlPreviewUserAgent: string | null;
|
public urlPreviewUserAgent: string | null;
|
||||||
|
|
||||||
|
@Column('enum', {
|
||||||
|
enum: ['follower', 'pv', 'none'],
|
||||||
|
default: 'follower',
|
||||||
|
})
|
||||||
|
public preferPopularUserFactor: 'follower' | 'pv' | 'none';
|
||||||
}
|
}
|
||||||
|
|
|
@ -211,6 +211,11 @@ export const packedMetaLiteSchema = {
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
optional: false, nullable: false,
|
optional: false, nullable: false,
|
||||||
},
|
},
|
||||||
|
preferPopularUserFactor: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
enum: ['follower', 'pv', 'none'],
|
||||||
|
},
|
||||||
backgroundImageUrl: {
|
backgroundImageUrl: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
optional: false, nullable: true,
|
optional: false, nullable: true,
|
||||||
|
|
|
@ -481,6 +481,11 @@ export const meta = {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
optional: false, nullable: true,
|
optional: false, nullable: true,
|
||||||
},
|
},
|
||||||
|
preferPopularUserFactor: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
enum: ['follower', 'pv', 'none'],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
@ -614,6 +619,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
urlPreviewRequireContentLength: instance.urlPreviewRequireContentLength,
|
urlPreviewRequireContentLength: instance.urlPreviewRequireContentLength,
|
||||||
urlPreviewUserAgent: instance.urlPreviewUserAgent,
|
urlPreviewUserAgent: instance.urlPreviewUserAgent,
|
||||||
urlPreviewSummaryProxyUrl: instance.urlPreviewSummaryProxyUrl,
|
urlPreviewSummaryProxyUrl: instance.urlPreviewSummaryProxyUrl,
|
||||||
|
preferPopularUserFactor: instance.preferPopularUserFactor,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -160,6 +160,10 @@ export const paramDef = {
|
||||||
urlPreviewRequireContentLength: { type: 'boolean' },
|
urlPreviewRequireContentLength: { type: 'boolean' },
|
||||||
urlPreviewUserAgent: { type: 'string', nullable: true },
|
urlPreviewUserAgent: { type: 'string', nullable: true },
|
||||||
urlPreviewSummaryProxyUrl: { type: 'string', nullable: true },
|
urlPreviewSummaryProxyUrl: { type: 'string', nullable: true },
|
||||||
|
preferPopularUserFactor: {
|
||||||
|
type: 'string',
|
||||||
|
enum: ['follower', 'pv', 'none'],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
required: [],
|
required: [],
|
||||||
} as const;
|
} as const;
|
||||||
|
@ -617,6 +621,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
set.urlPreviewSummaryProxyUrl = value === '' ? null : value;
|
set.urlPreviewSummaryProxyUrl = value === '' ? null : value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ps.preferPopularUserFactor !== undefined) {
|
||||||
|
set.preferPopularUserFactor = ps.preferPopularUserFactor;
|
||||||
|
}
|
||||||
|
|
||||||
const before = await this.metaService.fetch(true);
|
const before = await this.metaService.fetch(true);
|
||||||
|
|
||||||
await this.metaService.update(set);
|
await this.metaService.update(set);
|
||||||
|
|
|
@ -50,11 +50,25 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<template #caption>{{ i18n.ts.impressumDescription }}</template>
|
<template #caption>{{ i18n.ts.impressumDescription }}</template>
|
||||||
</MkInput>
|
</MkInput>
|
||||||
|
|
||||||
|
<FormSection>
|
||||||
|
<template #label>{{ i18n.ts.explore }}</template>
|
||||||
|
|
||||||
|
<div class="_gaps_m">
|
||||||
<MkTextarea v-model="pinnedUsers">
|
<MkTextarea v-model="pinnedUsers">
|
||||||
<template #label>{{ i18n.ts.pinnedUsers }}</template>
|
<template #label>{{ i18n.ts.pinnedUsers }}</template>
|
||||||
<template #caption>{{ i18n.ts.pinnedUsersDescription }}</template>
|
<template #caption>{{ i18n.ts.pinnedUsersDescription }}</template>
|
||||||
</MkTextarea>
|
</MkTextarea>
|
||||||
|
|
||||||
|
<MkRadios v-model="preferPopularUserFactor">
|
||||||
|
<template #label>{{ i18n.ts.preferPopularUserFactor }}</template>
|
||||||
|
<template #caption>{{ i18n.ts.preferPopularUserFactorDescription }}</template>
|
||||||
|
<option value="follower">{{ i18n.ts.followersCount }}</option>
|
||||||
|
<option value="pv">{{ i18n.ts.pageViewCount }}</option>
|
||||||
|
<option value="none">{{ i18n.ts.disabled }}</option>
|
||||||
|
</MkRadios>
|
||||||
|
</div>
|
||||||
|
</FormSection>
|
||||||
|
|
||||||
<FormSection>
|
<FormSection>
|
||||||
<template #label>{{ i18n.ts.files }}</template>
|
<template #label>{{ i18n.ts.files }}</template>
|
||||||
|
|
||||||
|
@ -222,6 +236,8 @@ import { definePageMetadata } from '@/scripts/page-metadata.js';
|
||||||
import MkButton from '@/components/MkButton.vue';
|
import MkButton from '@/components/MkButton.vue';
|
||||||
import MkFolder from '@/components/MkFolder.vue';
|
import MkFolder from '@/components/MkFolder.vue';
|
||||||
import MkSelect from '@/components/MkSelect.vue';
|
import MkSelect from '@/components/MkSelect.vue';
|
||||||
|
import MkRadios from '@/components/MkRadios.vue';
|
||||||
|
import { entities as MisskeyEntities } from 'misskey-js';
|
||||||
|
|
||||||
const name = ref<string | null>(null);
|
const name = ref<string | null>(null);
|
||||||
const shortName = ref<string | null>(null);
|
const shortName = ref<string | null>(null);
|
||||||
|
@ -249,6 +265,7 @@ const urlPreviewMaximumContentLength = ref<number>(1024 * 1024 * 10);
|
||||||
const urlPreviewRequireContentLength = ref<boolean>(true);
|
const urlPreviewRequireContentLength = ref<boolean>(true);
|
||||||
const urlPreviewUserAgent = ref<string | null>(null);
|
const urlPreviewUserAgent = ref<string | null>(null);
|
||||||
const urlPreviewSummaryProxyUrl = ref<string | null>(null);
|
const urlPreviewSummaryProxyUrl = ref<string | null>(null);
|
||||||
|
const preferPopularUserFactor = ref<MisskeyEntities.MetaLite['preferPopularUserFactor']>('follower');
|
||||||
|
|
||||||
async function init(): Promise<void> {
|
async function init(): Promise<void> {
|
||||||
const meta = await misskeyApi('admin/meta');
|
const meta = await misskeyApi('admin/meta');
|
||||||
|
@ -278,6 +295,7 @@ async function init(): Promise<void> {
|
||||||
urlPreviewRequireContentLength.value = meta.urlPreviewRequireContentLength;
|
urlPreviewRequireContentLength.value = meta.urlPreviewRequireContentLength;
|
||||||
urlPreviewUserAgent.value = meta.urlPreviewUserAgent;
|
urlPreviewUserAgent.value = meta.urlPreviewUserAgent;
|
||||||
urlPreviewSummaryProxyUrl.value = meta.urlPreviewSummaryProxyUrl;
|
urlPreviewSummaryProxyUrl.value = meta.urlPreviewSummaryProxyUrl;
|
||||||
|
preferPopularUserFactor.value = meta.preferPopularUserFactor;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function save() {
|
async function save() {
|
||||||
|
@ -308,6 +326,7 @@ async function save() {
|
||||||
urlPreviewRequireContentLength: urlPreviewRequireContentLength.value,
|
urlPreviewRequireContentLength: urlPreviewRequireContentLength.value,
|
||||||
urlPreviewUserAgent: urlPreviewUserAgent.value,
|
urlPreviewUserAgent: urlPreviewUserAgent.value,
|
||||||
urlPreviewSummaryProxyUrl: urlPreviewSummaryProxyUrl.value,
|
urlPreviewSummaryProxyUrl: urlPreviewSummaryProxyUrl.value,
|
||||||
|
preferPopularUserFactor: preferPopularUserFactor.value,
|
||||||
});
|
});
|
||||||
|
|
||||||
fetchInstance(true);
|
fetchInstance(true);
|
||||||
|
|
|
@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<template #header><i class="ti ti-bookmark ti-fw" style="margin-right: 0.5em;"></i>{{ i18n.ts.pinnedUsers }}</template>
|
<template #header><i class="ti ti-bookmark ti-fw" style="margin-right: 0.5em;"></i>{{ i18n.ts.pinnedUsers }}</template>
|
||||||
<MkUserList :pagination="pinnedUsers"/>
|
<MkUserList :pagination="pinnedUsers"/>
|
||||||
</MkFoldableSection>
|
</MkFoldableSection>
|
||||||
<MkFoldableSection class="_margin" persistKey="explore-popular-users">
|
<MkFoldableSection v-if="instance.preferPopularUserFactor !== 'none'" class="_margin" persistKey="explore-popular-users">
|
||||||
<template #header><i class="ti ti-chart-line ti-fw" style="margin-right: 0.5em;"></i>{{ i18n.ts.popularUsers }}</template>
|
<template #header><i class="ti ti-chart-line ti-fw" style="margin-right: 0.5em;"></i>{{ i18n.ts.popularUsers }}</template>
|
||||||
<MkUserList :pagination="popularUsers"/>
|
<MkUserList :pagination="popularUsers"/>
|
||||||
</MkFoldableSection>
|
</MkFoldableSection>
|
||||||
|
@ -45,7 +45,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</MkFoldableSection>
|
</MkFoldableSection>
|
||||||
|
|
||||||
<template v-if="tag == null">
|
<template v-if="tag == null">
|
||||||
<MkFoldableSection class="_margin">
|
<MkFoldableSection v-if="instance.preferPopularUserFactor !== 'none'" class="_margin">
|
||||||
<template #header><i class="ti ti-chart-line ti-fw" style="margin-right: 0.5em;"></i>{{ i18n.ts.popularUsers }}</template>
|
<template #header><i class="ti ti-chart-line ti-fw" style="margin-right: 0.5em;"></i>{{ i18n.ts.popularUsers }}</template>
|
||||||
<MkUserList :pagination="popularUsersF"/>
|
<MkUserList :pagination="popularUsersF"/>
|
||||||
</MkFoldableSection>
|
</MkFoldableSection>
|
||||||
|
@ -69,7 +69,9 @@ import MkUserList from '@/components/MkUserList.vue';
|
||||||
import MkFoldableSection from '@/components/MkFoldableSection.vue';
|
import MkFoldableSection from '@/components/MkFoldableSection.vue';
|
||||||
import MkTab from '@/components/MkTab.vue';
|
import MkTab from '@/components/MkTab.vue';
|
||||||
import { misskeyApi } from '@/scripts/misskey-api.js';
|
import { misskeyApi } from '@/scripts/misskey-api.js';
|
||||||
|
import { instance } from '@/instance.js';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
|
import type { Paging } from '@/components/MkPagination.vue';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
tag?: string;
|
tag?: string;
|
||||||
|
@ -94,34 +96,47 @@ const tagUsers = computed(() => ({
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const pinnedUsers = { endpoint: 'pinned-users', noPaging: true };
|
function createUserQueryPaging(params: Misskey.Endpoints['users']['req']): Paging {
|
||||||
const popularUsers = { endpoint: 'users', limit: 10, noPaging: true, params: {
|
return {
|
||||||
|
endpoint: 'users',
|
||||||
|
limit: 10,
|
||||||
|
noPaging: true,
|
||||||
|
params,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const pinnedUsers = {
|
||||||
|
endpoint: 'pinned-users',
|
||||||
|
noPaging: true,
|
||||||
|
limit: 10,
|
||||||
|
} satisfies Paging;
|
||||||
|
const popularUsers = createUserQueryPaging({
|
||||||
state: 'alive',
|
state: 'alive',
|
||||||
origin: 'local',
|
origin: 'local',
|
||||||
sort: '+pv',
|
sort: instance.preferPopularUserFactor === 'pv' ? '+pv' : '+follower',
|
||||||
} };
|
});
|
||||||
const recentlyUpdatedUsers = { endpoint: 'users', limit: 10, noPaging: true, params: {
|
const recentlyUpdatedUsers = createUserQueryPaging({
|
||||||
origin: 'local',
|
origin: 'local',
|
||||||
sort: '+updatedAt',
|
sort: '+updatedAt',
|
||||||
} };
|
});
|
||||||
const recentlyRegisteredUsers = { endpoint: 'users', limit: 10, noPaging: true, params: {
|
const recentlyRegisteredUsers = createUserQueryPaging({
|
||||||
origin: 'local',
|
origin: 'local',
|
||||||
state: 'alive',
|
state: 'alive',
|
||||||
sort: '+createdAt',
|
sort: '+createdAt',
|
||||||
} };
|
});
|
||||||
const popularUsersF = { endpoint: 'users', limit: 10, noPaging: true, params: {
|
const popularUsersF = createUserQueryPaging({
|
||||||
state: 'alive',
|
state: 'alive',
|
||||||
origin: 'remote',
|
origin: 'remote',
|
||||||
sort: '+follower',
|
sort: '+follower', // リモートのpvは信用ならない
|
||||||
} };
|
});
|
||||||
const recentlyUpdatedUsersF = { endpoint: 'users', limit: 10, noPaging: true, params: {
|
const recentlyUpdatedUsersF = createUserQueryPaging({
|
||||||
origin: 'combined',
|
origin: 'combined',
|
||||||
sort: '+updatedAt',
|
sort: '+updatedAt',
|
||||||
} };
|
});
|
||||||
const recentlyRegisteredUsersF = { endpoint: 'users', limit: 10, noPaging: true, params: {
|
const recentlyRegisteredUsersF = createUserQueryPaging({
|
||||||
origin: 'combined',
|
origin: 'combined',
|
||||||
sort: '+createdAt',
|
sort: '+createdAt',
|
||||||
} };
|
});
|
||||||
|
|
||||||
misskeyApi('hashtags/list', {
|
misskeyApi('hashtags/list', {
|
||||||
sort: '+attachedLocalUsers',
|
sort: '+attachedLocalUsers',
|
||||||
|
|
|
@ -4929,6 +4929,8 @@ export type components = {
|
||||||
translatorAvailable: boolean;
|
translatorAvailable: boolean;
|
||||||
mediaProxy: string;
|
mediaProxy: string;
|
||||||
enableUrlPreview: boolean;
|
enableUrlPreview: boolean;
|
||||||
|
/** @enum {string} */
|
||||||
|
preferPopularUserFactor: 'follower' | 'pv' | 'none';
|
||||||
backgroundImageUrl: string | null;
|
backgroundImageUrl: string | null;
|
||||||
impressumUrl: string | null;
|
impressumUrl: string | null;
|
||||||
logoImageUrl: string | null;
|
logoImageUrl: string | null;
|
||||||
|
@ -5124,6 +5126,8 @@ export type operations = {
|
||||||
urlPreviewRequireContentLength: boolean;
|
urlPreviewRequireContentLength: boolean;
|
||||||
urlPreviewUserAgent: string | null;
|
urlPreviewUserAgent: string | null;
|
||||||
urlPreviewSummaryProxyUrl: string | null;
|
urlPreviewSummaryProxyUrl: string | null;
|
||||||
|
/** @enum {string} */
|
||||||
|
preferPopularUserFactor: 'follower' | 'pv' | 'none';
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -9367,6 +9371,8 @@ export type operations = {
|
||||||
urlPreviewRequireContentLength?: boolean;
|
urlPreviewRequireContentLength?: boolean;
|
||||||
urlPreviewUserAgent?: string | null;
|
urlPreviewUserAgent?: string | null;
|
||||||
urlPreviewSummaryProxyUrl?: string | null;
|
urlPreviewSummaryProxyUrl?: string | null;
|
||||||
|
/** @enum {string} */
|
||||||
|
preferPopularUserFactor?: 'follower' | 'pv' | 'none';
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue