1
0
mirror of https://github.com/MisskeyIO/misskey synced 2024-11-27 14:28:49 +09:00

enhance: ロールの並び順を設定可能に

This commit is contained in:
syuilo 2023-03-12 16:38:08 +09:00
parent 57cac0aa23
commit 7c2d8d73ce
11 changed files with 42 additions and 7 deletions

View File

@ -15,6 +15,7 @@ You should also include the user name that made the change.
### Improvements ### Improvements
- ユーザーごとにRenoteをミュートできるように - ユーザーごとにRenoteをミュートできるように
- ノートごとに絵文字リアクションを受け取るか設定できるように - ノートごとに絵文字リアクションを受け取るか設定できるように
- ロールの並び順を設定可能に
- enhance(client): DM作成時にメンションも含むように - enhance(client): DM作成時にメンションも含むように
- enhance(client): フォロー申請のボタンのデザインを改善 - enhance(client): フォロー申請のボタンのデザインを改善
- enhance(backend): OpenAPIエンドポイントを復旧 - enhance(backend): OpenAPIエンドポイントを復旧

View File

@ -969,6 +969,7 @@ cannotBeChangedLater: "後から変更できません。"
reactionAcceptance: "リアクションの受け入れ" reactionAcceptance: "リアクションの受け入れ"
likeOnly: "いいねのみ" likeOnly: "いいねのみ"
likeOnlyForRemote: "リモートからはいいねのみ" likeOnlyForRemote: "リモートからはいいねのみ"
rolesAssignedToMe: "自分に割り当てられたロール"
_achievements: _achievements:
earnedAt: "獲得日時" earnedAt: "獲得日時"
@ -1230,6 +1231,8 @@ _role:
iconUrl: "アイコン画像のURL" iconUrl: "アイコン画像のURL"
asBadge: "バッジとして表示" asBadge: "バッジとして表示"
descriptionOfAsBadge: "オンにすると、ユーザー名の横にロールのアイコンが表示されます。" descriptionOfAsBadge: "オンにすると、ユーザー名の横にロールのアイコンが表示されます。"
displayOrder: "表示順"
descriptionOfDisplayOrder: "数値が大きいほどUI上で先頭に表示されます。"
canEditMembersByModerator: "モデレーターのメンバー編集を許可" canEditMembersByModerator: "モデレーターのメンバー編集を許可"
descriptionOfCanEditMembersByModerator: "オンにすると、管理者に加えてモデレーターもこのロールへユーザーをアサイン/アサイン解除できるようになります。オフにすると管理者のみが行えます。" descriptionOfCanEditMembersByModerator: "オンにすると、管理者に加えてモデレーターもこのロールへユーザーをアサイン/アサイン解除できるようになります。オフにすると管理者のみが行えます。"
priority: "優先度" priority: "優先度"

View File

@ -0,0 +1,11 @@
export class roleDisplayOrder1678602320354 {
name = 'roleDisplayOrder1678602320354'
async up(queryRunner) {
await queryRunner.query(`ALTER TABLE "role" ADD "displayOrder" integer NOT NULL DEFAULT '0'`);
}
async down(queryRunner) {
await queryRunner.query(`ALTER TABLE "role" DROP COLUMN "displayOrder"`);
}
}

View File

@ -61,6 +61,7 @@ export class RoleEntityService {
isModerator: role.isModerator, isModerator: role.isModerator,
asBadge: role.asBadge, asBadge: role.asBadge,
canEditMembersByModerator: role.canEditMembersByModerator, canEditMembersByModerator: role.canEditMembersByModerator,
displayOrder: role.displayOrder,
policies: policies, policies: policies,
usersCount: assignedCount, usersCount: assignedCount,
}); });

View File

@ -390,9 +390,10 @@ export class UserEntityService implements OnModuleInit {
emojis: this.customEmojiService.populateEmojis(user.emojis, user.host), emojis: this.customEmojiService.populateEmojis(user.emojis, user.host),
onlineStatus: this.getOnlineStatus(user), onlineStatus: this.getOnlineStatus(user),
// パフォーマンス上の理由でローカルユーザーのみ // パフォーマンス上の理由でローカルユーザーのみ
badgeRoles: user.host == null ? this.roleService.getUserBadgeRoles(user.id).then(rs => rs.map(r => ({ badgeRoles: user.host == null ? this.roleService.getUserBadgeRoles(user.id).then(rs => rs.sort((a, b) => b.displayOrder - a.displayOrder).map(r => ({
name: r.name, name: r.name,
iconUrl: r.iconUrl, iconUrl: r.iconUrl,
displayOrder: r.displayOrder,
}))) : undefined, }))) : undefined,
...(opts.detail ? { ...(opts.detail ? {
@ -429,7 +430,7 @@ export class UserEntityService implements OnModuleInit {
userId: user.id, userId: user.id,
}).then(result => result >= 1) }).then(result => result >= 1)
: false, : false,
roles: this.roleService.getUserRoles(user.id).then(roles => roles.filter(role => role.isPublic).map(role => ({ roles: this.roleService.getUserRoles(user.id).then(roles => roles.filter(role => role.isPublic).sort((a, b) => b.displayOrder - a.displayOrder).map(role => ({
id: role.id, id: role.id,
name: role.name, name: role.name,
color: role.color, color: role.color,
@ -437,6 +438,7 @@ export class UserEntityService implements OnModuleInit {
description: role.description, description: role.description,
isModerator: role.isModerator, isModerator: role.isModerator,
isAdministrator: role.isAdministrator, isAdministrator: role.isAdministrator,
displayOrder: role.displayOrder,
}))), }))),
} : {}), } : {}),

View File

@ -144,6 +144,12 @@ export class Role {
}) })
public canEditMembersByModerator: boolean; public canEditMembersByModerator: boolean;
// UIに表示する際の並び順用(大きいほど先頭)
@Column('integer', {
default: 0,
})
public displayOrder: number;
@Column('jsonb', { @Column('jsonb', {
default: { }, default: { },
}) })

View File

@ -27,6 +27,7 @@ export const paramDef = {
isAdministrator: { type: 'boolean' }, isAdministrator: { type: 'boolean' },
asBadge: { type: 'boolean' }, asBadge: { type: 'boolean' },
canEditMembersByModerator: { type: 'boolean' }, canEditMembersByModerator: { type: 'boolean' },
displayOrder: { type: 'number' },
policies: { policies: {
type: 'object', type: 'object',
}, },
@ -43,6 +44,7 @@ export const paramDef = {
'isAdministrator', 'isAdministrator',
'asBadge', 'asBadge',
'canEditMembersByModerator', 'canEditMembersByModerator',
'displayOrder',
'policies', 'policies',
], ],
} as const; } as const;
@ -76,6 +78,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
isModerator: ps.isModerator, isModerator: ps.isModerator,
asBadge: ps.asBadge, asBadge: ps.asBadge,
canEditMembersByModerator: ps.canEditMembersByModerator, canEditMembersByModerator: ps.canEditMembersByModerator,
displayOrder: ps.displayOrder,
policies: ps.policies, policies: ps.policies,
}).then(x => this.rolesRepository.findOneByOrFail(x.identifiers[0])); }).then(x => this.rolesRepository.findOneByOrFail(x.identifiers[0]));

View File

@ -35,6 +35,7 @@ export const paramDef = {
isAdministrator: { type: 'boolean' }, isAdministrator: { type: 'boolean' },
asBadge: { type: 'boolean' }, asBadge: { type: 'boolean' },
canEditMembersByModerator: { type: 'boolean' }, canEditMembersByModerator: { type: 'boolean' },
displayOrder: { type: 'number' },
policies: { policies: {
type: 'object', type: 'object',
}, },
@ -52,6 +53,7 @@ export const paramDef = {
'isAdministrator', 'isAdministrator',
'asBadge', 'asBadge',
'canEditMembersByModerator', 'canEditMembersByModerator',
'displayOrder',
'policies', 'policies',
], ],
} as const; } as const;
@ -85,6 +87,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
isAdministrator: ps.isAdministrator, isAdministrator: ps.isAdministrator,
asBadge: ps.asBadge, asBadge: ps.asBadge,
canEditMembersByModerator: ps.canEditMembersByModerator, canEditMembersByModerator: ps.canEditMembersByModerator,
displayOrder: ps.displayOrder,
policies: ps.policies, policies: ps.policies,
}); });
const updated = await this.rolesRepository.findOneByOrFail({ id: ps.roleId }); const updated = await this.rolesRepository.findOneByOrFail({ id: ps.roleId });

View File

@ -55,6 +55,7 @@ if (props.id) {
isPublic: false, isPublic: false,
asBadge: false, asBadge: false,
canEditMembersByModerator: false, canEditMembersByModerator: false,
displayOrder: 0,
policies: {}, policies: {},
}; };
} }

View File

@ -17,6 +17,11 @@
<template #label>{{ i18n.ts._role.iconUrl }}</template> <template #label>{{ i18n.ts._role.iconUrl }}</template>
</MkInput> </MkInput>
<MkInput v-model="role.displayOrder" type="number">
<template #label>{{ i18n.ts._role.displayOrder }}</template>
<template #caption>{{ i18n.ts._role.descriptionOfDisplayOrder }}</template>
</MkInput>
<MkSelect v-model="rolePermission" :readonly="readonly"> <MkSelect v-model="rolePermission" :readonly="readonly">
<template #label><i class="ti ti-shield-lock"></i> {{ i18n.ts._role.permission }}</template> <template #label><i class="ti ti-shield-lock"></i> {{ i18n.ts._role.permission }}</template>
<template #caption><div v-html="i18n.ts._role.descriptionOfPermission.replaceAll('\n', '<br>')"></div></template> <template #caption><div v-html="i18n.ts._role.descriptionOfPermission.replaceAll('\n', '<br>')"></div></template>
@ -444,6 +449,7 @@ const save = throttle(100, () => {
description: role.description, description: role.description,
color: role.color === '' ? null : role.color, color: role.color === '' ? null : role.color,
iconUrl: role.iconUrl === '' ? null : role.iconUrl, iconUrl: role.iconUrl === '' ? null : role.iconUrl,
displayOrder: role.displayOrder,
target: role.target, target: role.target,
condFormula: role.condFormula, condFormula: role.condFormula,
isAdministrator: role.isAdministrator, isAdministrator: role.isAdministrator,

View File

@ -1,5 +1,5 @@
<template> <template>
<MkSpacer :content-max="1200"> <MkSpacer :content-max="700">
<div class="_gaps_s"> <div class="_gaps_s">
<MkRolePreview v-for="role in roles" :key="role.id" :role="role" :for-moderation="false"/> <MkRolePreview v-for="role in roles" :key="role.id" :role="role" :for-moderation="false"/>
</div> </div>
@ -13,10 +13,8 @@ import * as os from '@/os';
let roles = $ref(); let roles = $ref();
os.api('roles/list', { os.api('roles/list').then(res => {
limit: 30, roles = res.filter(x => x.target === 'manual').sort((a, b) => b.displayOrder - a.displayOrder);
}).then(res => {
roles = res.filter(x => x.target === 'manual');
}); });
</script> </script>