mirror of
https://github.com/hotomoe/hotomoe
synced 2024-12-11 21:28:14 +09:00
spec(profile): 相互リンク機能のロールによる制御の仕様変更 (MisskeyIO#701)
This commit is contained in:
parent
80389a9140
commit
09c419e11b
@ -2273,6 +2273,7 @@ _profile:
|
|||||||
sectionName: "Section name"
|
sectionName: "Section name"
|
||||||
sectionNameNoneDescription: "Do not display the section name"
|
sectionNameNoneDescription: "Do not display the section name"
|
||||||
sectionNameNone: "Section without name"
|
sectionNameNone: "Section without name"
|
||||||
|
policyDisplayLimitExceeded: "The number of items displayed exceeds the current support plan's limit ({max}). This item will not be displayed. You can upgrade your plan [here](https://go.misskey.io/donate)."
|
||||||
_exportOrImport:
|
_exportOrImport:
|
||||||
allNotes: "All notes"
|
allNotes: "All notes"
|
||||||
favoritedNotes: "Favorite notes"
|
favoritedNotes: "Favorite notes"
|
||||||
|
4
locales/index.d.ts
vendored
4
locales/index.d.ts
vendored
@ -8862,6 +8862,10 @@ export interface Locale extends ILocale {
|
|||||||
* 名前が表示されないセクション
|
* 名前が表示されないセクション
|
||||||
*/
|
*/
|
||||||
"sectionNameNone": string;
|
"sectionNameNone": string;
|
||||||
|
/**
|
||||||
|
* 現在の支援プランの表示上限({max}個)を超えているため、この項目は表示されません。[ここ](https://go.misskey.io/donate)からプランをアップグレードできます。
|
||||||
|
*/
|
||||||
|
"policyDisplayLimitExceeded": ParameterizedString<"max">;
|
||||||
};
|
};
|
||||||
"_exportOrImport": {
|
"_exportOrImport": {
|
||||||
/**
|
/**
|
||||||
|
@ -2329,6 +2329,7 @@ _profile:
|
|||||||
sectionName: "セクション名"
|
sectionName: "セクション名"
|
||||||
sectionNameNoneDescription: "セクション名を表示しないようにする"
|
sectionNameNoneDescription: "セクション名を表示しないようにする"
|
||||||
sectionNameNone: "名前が表示されないセクション"
|
sectionNameNone: "名前が表示されないセクション"
|
||||||
|
policyDisplayLimitExceeded: "現在の支援プランの表示上限({max}個)を超えているため、この項目は表示されません。[ここ](https://go.misskey.io/donate)からプランをアップグレードできます。"
|
||||||
|
|
||||||
_exportOrImport:
|
_exportOrImport:
|
||||||
allNotes: "全てのノート"
|
allNotes: "全てのノート"
|
||||||
|
@ -2255,8 +2255,9 @@ _profile:
|
|||||||
addMutualLink: "서로링크 추가"
|
addMutualLink: "서로링크 추가"
|
||||||
addMutualLinkSection: "섹션 추가"
|
addMutualLinkSection: "섹션 추가"
|
||||||
sectionName: "섹션 이름"
|
sectionName: "섹션 이름"
|
||||||
sectionNameNoneDescription: "섹션 이름이 표시되지 않도록 합니다."
|
sectionNameNoneDescription: "섹션 이름이 표시되지 않도록 합니다"
|
||||||
sectionNameNone: "이름이 표시되지 않는 섹션"
|
sectionNameNone: "이름이 표시되지 않는 섹션"
|
||||||
|
policyDisplayLimitExceeded: "현재 지원 플랜의 표시 제한({max}개)을 초과하였기 때문에 이 항목은 표시되지 않습니다. [여기](https://go.misskey.io/donate)에서 플랜을 업그레이드할 수 있습니다."
|
||||||
_exportOrImport:
|
_exportOrImport:
|
||||||
allNotes: "모든 노트"
|
allNotes: "모든 노트"
|
||||||
favoritedNotes: "즐겨찾기한 노트"
|
favoritedNotes: "즐겨찾기한 노트"
|
||||||
|
@ -111,7 +111,7 @@ export const DEFAULT_POLICIES: RolePolicies = {
|
|||||||
rateLimitFactor: 1,
|
rateLimitFactor: 1,
|
||||||
avatarDecorationLimit: 1,
|
avatarDecorationLimit: 1,
|
||||||
mutualLinkSectionLimit: 1,
|
mutualLinkSectionLimit: 1,
|
||||||
mutualLinkLimit: 15,
|
mutualLinkLimit: 3,
|
||||||
};
|
};
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
|
@ -533,7 +533,7 @@ export class UserEntityService implements OnModuleInit {
|
|||||||
lang: profile!.lang,
|
lang: profile!.lang,
|
||||||
fields: profile!.fields,
|
fields: profile!.fields,
|
||||||
verifiedLinks: profile!.verifiedLinks,
|
verifiedLinks: profile!.verifiedLinks,
|
||||||
mutualLinkSections: profile!.mutualLinkSections,
|
mutualLinkSections: isMe ? profile!.mutualLinkSections : profile!.mutualLinkSections.slice(0, policies!.mutualLinkSectionLimit).map(section => ({ ...section, mutualLinks: section.mutualLinks.slice(0, policies!.mutualLinkLimit) })),
|
||||||
followersCount: followersCount ?? 0,
|
followersCount: followersCount ?? 0,
|
||||||
followingCount: followingCount ?? 0,
|
followingCount: followingCount ?? 0,
|
||||||
notesCount: user.notesCount,
|
notesCount: user.notesCount,
|
||||||
|
@ -117,6 +117,12 @@ export const meta = {
|
|||||||
id: 'bf326f31-d430-4f97-9933-5d61e4d48a23',
|
id: 'bf326f31-d430-4f97-9933-5d61e4d48a23',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
invalidUrl: {
|
||||||
|
message: 'Invalid URL',
|
||||||
|
code: 'INVALID_URL',
|
||||||
|
id: 'b2452e00-2bd0-4da8-a2d0-972859da7358',
|
||||||
|
},
|
||||||
|
|
||||||
forbiddenToSetYourself: {
|
forbiddenToSetYourself: {
|
||||||
message: 'You can\'t set yourself as your own alias.',
|
message: 'You can\'t set yourself as your own alias.',
|
||||||
code: 'FORBIDDEN_TO_SET_YOURSELF',
|
code: 'FORBIDDEN_TO_SET_YOURSELF',
|
||||||
@ -228,12 +234,14 @@ export const paramDef = {
|
|||||||
},
|
},
|
||||||
mutualLinkSections: {
|
mutualLinkSections: {
|
||||||
type: 'array',
|
type: 'array',
|
||||||
|
maxItems: 10,
|
||||||
items: {
|
items: {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
properties: {
|
properties: {
|
||||||
name: { type: 'string', nullable: true },
|
name: { type: 'string', nullable: true },
|
||||||
mutualLinks: {
|
mutualLinks: {
|
||||||
type: 'array',
|
type: 'array',
|
||||||
|
maxItems: 30,
|
||||||
items: {
|
items: {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
properties: {
|
properties: {
|
||||||
@ -359,24 +367,13 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (ps.mutualLinkSections) {
|
if (ps.mutualLinkSections) {
|
||||||
if (ps.mutualLinkSections.length > policy.mutualLinkSectionLimit) {
|
|
||||||
throw new ApiError(meta.errors.restrictedByRole);
|
|
||||||
}
|
|
||||||
|
|
||||||
const mutualLinkSections = ps.mutualLinkSections.map(async (section) => {
|
const mutualLinkSections = ps.mutualLinkSections.map(async (section) => {
|
||||||
if (section.mutualLinks.length > policy.mutualLinkLimit) {
|
|
||||||
throw new ApiError(meta.errors.restrictedByRole);
|
|
||||||
}
|
|
||||||
|
|
||||||
const mutualLinks = await Promise.all(section.mutualLinks.map(async (mutualLink) => {
|
const mutualLinks = await Promise.all(section.mutualLinks.map(async (mutualLink) => {
|
||||||
const file = await this.driveFilesRepository.findOneBy({ id: mutualLink.fileId });
|
if (!RegExp(/^https?:\/\//).test(mutualLink.url)) throw new ApiError(meta.errors.invalidUrl);
|
||||||
|
|
||||||
if (!file) {
|
const file = await this.driveFilesRepository.findOneBy({ id: mutualLink.fileId });
|
||||||
throw new ApiError(meta.errors.noSuchFile);
|
if (!file) throw new ApiError(meta.errors.noSuchFile);
|
||||||
}
|
if (!file.type.startsWith("image/")) throw new ApiError(meta.errors.fileNotAnImage);
|
||||||
if (!file.type.startsWith('image/')) {
|
|
||||||
throw new ApiError(meta.errors.fileNotAnImage);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: this.idService.gen(),
|
id: this.idService.gen(),
|
||||||
|
@ -99,7 +99,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
<template #icon><i class="ti ti-message"></i></template>
|
<template #icon><i class="ti ti-message"></i></template>
|
||||||
{{ i18n.ts.support }}
|
{{ i18n.ts.support }}
|
||||||
</FormLink>
|
</FormLink>
|
||||||
<FormLink to="https://misskeyhq.fanbox.cc" external>
|
<FormLink to="https://go.misskey.io/donate" external>
|
||||||
<template #icon><i class="ti ti-pig-money"></i></template>
|
<template #icon><i class="ti ti-pig-money"></i></template>
|
||||||
{{ i18n.tsx.supportThisInstance({ name: instance.name ?? host }) }}
|
{{ i18n.tsx.supportThisInstance({ name: instance.name ?? host }) }}
|
||||||
<template #suffix>pixivFANBOX</template>
|
<template #suffix>pixivFANBOX</template>
|
||||||
|
@ -755,25 +755,6 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</div>
|
</div>
|
||||||
</MkFolder>
|
</MkFolder>
|
||||||
|
|
||||||
<MkFolder v-if="matchQuery([i18n.ts._role._options.mutualLinkLimit, 'mutualLinkLimit'])">
|
|
||||||
<template #label>{{ i18n.ts._role._options.mutualLinkLimit }}</template>
|
|
||||||
<template #suffix>
|
|
||||||
<span v-if="role.policies.mutualLinkLimit.useDefault" :class="$style.useDefaultLabel">{{ i18n.ts._role.useBaseValue }}</span>
|
|
||||||
<span v-else>{{ role.policies.mutualLinkLimit.value }}</span>
|
|
||||||
<span :class="$style.priorityIndicator"><i :class="getPriorityIcon(role.policies.mutualLinkLimit)"></i></span>
|
|
||||||
</template>
|
|
||||||
<div class="_gaps">
|
|
||||||
<MkSwitch v-model="role.policies.mutualLinkLimit.useDefault" :readonly="readonly">
|
|
||||||
<template #label>{{ i18n.ts._role.useBaseValue }}</template>
|
|
||||||
</MkSwitch>
|
|
||||||
<MkInput v-model="role.policies.mutualLinkLimit.value" :disabled="role.policies.mutualLinkLimit.useDefault" type="number" :readonly="readonly">
|
|
||||||
</MkInput>
|
|
||||||
<MkRange v-model="role.policies.mutualLinkLimit.priority" :min="0" :max="2" :step="1" easing :textConverter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
|
|
||||||
<template #label>{{ i18n.ts._role.priority }}</template>
|
|
||||||
</MkRange>
|
|
||||||
</div>
|
|
||||||
</MkFolder>
|
|
||||||
|
|
||||||
<MkFolder v-if="matchQuery([i18n.ts._role._options.mutualLinkSectionLimit, 'mutualLinkSectionLimit'])">
|
<MkFolder v-if="matchQuery([i18n.ts._role._options.mutualLinkSectionLimit, 'mutualLinkSectionLimit'])">
|
||||||
<template #label>{{ i18n.ts._role._options.mutualLinkSectionLimit }}</template>
|
<template #label>{{ i18n.ts._role._options.mutualLinkSectionLimit }}</template>
|
||||||
<template #suffix>
|
<template #suffix>
|
||||||
@ -793,6 +774,25 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</div>
|
</div>
|
||||||
</MkFolder>
|
</MkFolder>
|
||||||
|
|
||||||
|
<MkFolder v-if="matchQuery([i18n.ts._role._options.mutualLinkLimit, 'mutualLinkLimit'])">
|
||||||
|
<template #label>{{ i18n.ts._role._options.mutualLinkLimit }}</template>
|
||||||
|
<template #suffix>
|
||||||
|
<span v-if="role.policies.mutualLinkLimit.useDefault" :class="$style.useDefaultLabel">{{ i18n.ts._role.useBaseValue }}</span>
|
||||||
|
<span v-else>{{ role.policies.mutualLinkLimit.value }}</span>
|
||||||
|
<span :class="$style.priorityIndicator"><i :class="getPriorityIcon(role.policies.mutualLinkLimit)"></i></span>
|
||||||
|
</template>
|
||||||
|
<div class="_gaps">
|
||||||
|
<MkSwitch v-model="role.policies.mutualLinkLimit.useDefault" :readonly="readonly">
|
||||||
|
<template #label>{{ i18n.ts._role.useBaseValue }}</template>
|
||||||
|
</MkSwitch>
|
||||||
|
<MkInput v-model="role.policies.mutualLinkLimit.value" :disabled="role.policies.mutualLinkLimit.useDefault" type="number" :readonly="readonly">
|
||||||
|
</MkInput>
|
||||||
|
<MkRange v-model="role.policies.mutualLinkLimit.priority" :min="0" :max="2" :step="1" easing :textConverter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
|
||||||
|
<template #label>{{ i18n.ts._role.priority }}</template>
|
||||||
|
</MkRange>
|
||||||
|
</div>
|
||||||
|
</MkFolder>
|
||||||
|
|
||||||
<MkFolder v-if="matchQuery([i18n.ts._role._options.canHideAds, 'canHideAds'])">
|
<MkFolder v-if="matchQuery([i18n.ts._role._options.canHideAds, 'canHideAds'])">
|
||||||
<template #label>{{ i18n.ts._role._options.canHideAds }}</template>
|
<template #label>{{ i18n.ts._role._options.canHideAds }}</template>
|
||||||
<template #suffix>
|
<template #suffix>
|
||||||
|
@ -94,7 +94,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
|
|
||||||
<div :class="$style.metadataRoot">
|
<div :class="$style.metadataRoot">
|
||||||
<div :class="$style.metadataMargin">
|
<div :class="$style.metadataMargin">
|
||||||
<MkButton inline style="margin-right: 8px;" :disabled="mutualLinkSections.length >= $i.policies.mutualLinkSectionLimit" @click="addMutualLinkSections"><i class="ti ti-plus"></i> {{ i18n.ts._profile.addMutualLinkSection }}</MkButton>
|
<MkButton inline style="margin-right: 8px;" @click="addMutualLinkSections"><i class="ti ti-plus"></i> {{ i18n.ts._profile.addMutualLinkSection }}</MkButton>
|
||||||
<MkButton v-if="!mutualLinkSectionEditMode" inline danger style="margin-right: 8px;" @click="mutualLinkSectionEditMode = !mutualLinkSectionEditMode"><i class="ti ti-trash"></i> {{ i18n.ts.delete }}</MkButton>
|
<MkButton v-if="!mutualLinkSectionEditMode" inline danger style="margin-right: 8px;" @click="mutualLinkSectionEditMode = !mutualLinkSectionEditMode"><i class="ti ti-trash"></i> {{ i18n.ts.delete }}</MkButton>
|
||||||
<MkButton v-else inline style="margin-right: 8px;" @click="mutualLinkSectionEditMode = !mutualLinkSectionEditMode"><i class="ti ti-arrows-sort"></i> {{ i18n.ts.rearrange }}</MkButton>
|
<MkButton v-else inline style="margin-right: 8px;" @click="mutualLinkSectionEditMode = !mutualLinkSectionEditMode"><i class="ti ti-arrows-sort"></i> {{ i18n.ts.rearrange }}</MkButton>
|
||||||
<MkButton inline primary @click="saveMutualLinks"><i class="ti ti-check"></i> {{ i18n.ts.save }}</MkButton>
|
<MkButton inline primary @click="saveMutualLinks"><i class="ti ti-check"></i> {{ i18n.ts.save }}</MkButton>
|
||||||
@ -118,9 +118,10 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
<template #label>{{ sectionElement.name || i18n.ts._profile.sectionNameNone }}</template>
|
<template #label>{{ sectionElement.name || i18n.ts._profile.sectionNameNone }}</template>
|
||||||
|
|
||||||
<div class="_gaps_s" :class="$style.metadataMargin">
|
<div class="_gaps_s" :class="$style.metadataMargin">
|
||||||
|
<MkInfo v-if="sectionIndex >= $i.policies.mutualLinkSectionLimit" warn><Mfm :text="i18n.tsx._profile.policyDisplayLimitExceeded({ max: $i.policies.mutualLinkSectionLimit })"/></MkInfo>
|
||||||
<MkInput v-if="sectionElement.name !== null" v-model="sectionElement.name" :placeholder="i18n.ts._profile.sectionName" :max="32"></MkInput>
|
<MkInput v-if="sectionElement.name !== null" v-model="sectionElement.name" :placeholder="i18n.ts._profile.sectionName" :max="32"></MkInput>
|
||||||
<MkSwitch v-model="sectionElement.none" @update:modelValue="()=>{ sectionElement.none ? sectionElement.name = null : sectionElement.name = 'New Section' }">{{ i18n.ts._profile.sectionNameNoneDescription }}</MkSwitch>
|
<MkSwitch v-model="sectionElement.none" @update:modelValue="()=>{ sectionElement.none ? sectionElement.name = null : sectionElement.name = 'New Section' }">{{ i18n.ts._profile.sectionNameNoneDescription }}</MkSwitch>
|
||||||
<MkButton inline style="margin-right: 8px;" :disabled="sectionElement.mutualLinks.length >= $i.policies.mutualLinkLimit" @click="addMutualLinks(sectionIndex)"><i class="ti ti-plus"></i> {{ i18n.ts._profile.addMutualLink }}</MkButton>
|
<MkButton inline style="margin-right: 8px;" @click="addMutualLinks(sectionIndex)"><i class="ti ti-plus"></i> {{ i18n.ts._profile.addMutualLink }}</MkButton>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Sortable
|
<Sortable
|
||||||
@ -138,6 +139,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
<button v-if="mutualLinkSectionEditMode" :disabled="fields.length <= 1" class="_button" :class="$style.dragItemRemove" @click="deleteMutualLink(sectionIndex,linkIndex)"><i class="ti ti-x"></i></button>
|
<button v-if="mutualLinkSectionEditMode" :disabled="fields.length <= 1" class="_button" :class="$style.dragItemRemove" @click="deleteMutualLink(sectionIndex,linkIndex)"><i class="ti ti-x"></i></button>
|
||||||
|
|
||||||
<div class="_gaps_s" :style="{flex: 1}">
|
<div class="_gaps_s" :style="{flex: 1}">
|
||||||
|
<MkInfo v-if="linkIndex >= $i.policies.mutualLinkLimit" warn><Mfm :text="i18n.tsx._profile.policyDisplayLimitExceeded({ max: $i.policies.mutualLinkLimit })"/></MkInfo>
|
||||||
<MkInput v-model="linkElement.url" small>
|
<MkInput v-model="linkElement.url" small>
|
||||||
<template #label>{{ i18n.ts._profile.mutualLinksUrl }}</template>
|
<template #label>{{ i18n.ts._profile.mutualLinksUrl }}</template>
|
||||||
</MkInput>
|
</MkInput>
|
||||||
|
@ -80,7 +80,19 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
<p v-else class="empty">{{ i18n.ts.noAccountDescription }}</p>
|
<p v-else class="empty">{{ i18n.ts.noAccountDescription }}</p>
|
||||||
</MkOmit>
|
</MkOmit>
|
||||||
</div>
|
</div>
|
||||||
<MkContainer v-if="user?.mutualLinkSections?.length > 0" :showHeader="false" :max-height="200" class="fields" :style="{borderRadius: 0}">
|
<MkContainer v-if="$i && $i.id == user.id && user?.mutualLinkSections?.slice(0, $i.policies.mutualLinkSectionLimit).length > 0" :showHeader="false" :max-height="200" class="fields" :style="{borderRadius: 0}">
|
||||||
|
<div v-for="(section, index) in user?.mutualLinkSections.slice(0, $i.policies.mutualLinkSectionLimit)" :key="index" :class="$style.mutualLinkSections">
|
||||||
|
<span v-if="section.name">{{ section.name }}</span>
|
||||||
|
<div :class="$style.mutualLinks">
|
||||||
|
<div v-for="mutualLink in section.mutualLinks.slice(0, $i.policies.mutualLinkLimit)" :key="mutualLink.id">
|
||||||
|
<MkLink :hideIcon="true" :url="mutualLink.url">
|
||||||
|
<img :class="$style.mutualLinkImg" :src="mutualLink.imgSrc" :alt="mutualLink.description"/>
|
||||||
|
</MkLink>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</MkContainer>
|
||||||
|
<MkContainer v-else-if="user?.mutualLinkSections?.length > 0" :showHeader="false" :max-height="200" class="fields" :style="{borderRadius: 0}">
|
||||||
<div v-for="(section, index) in user?.mutualLinkSections" :key="index" :class="$style.mutualLinkSections">
|
<div v-for="(section, index) in user?.mutualLinkSections" :key="index" :class="$style.mutualLinkSections">
|
||||||
<span v-if="section.name">{{ section.name }}</span>
|
<span v-if="section.name">{{ section.name }}</span>
|
||||||
<div :class="$style.mutualLinks">
|
<div :class="$style.mutualLinks">
|
||||||
|
Loading…
Reference in New Issue
Block a user