enhance: ユーザーにロールが期限付きでアサインされている場合、その期限をユーザーのモデレーションページで確認できるように
Close #11059
This commit is contained in:
		
							parent
							
								
									2ddf575cdc
								
							
						
					
					
						commit
						871027fa0c
					
				
					 6 changed files with 51 additions and 11 deletions
				
			
		| 
						 | 
				
			
			@ -15,12 +15,13 @@
 | 
			
		|||
## 13.x.x (unreleased)
 | 
			
		||||
 | 
			
		||||
### General
 | 
			
		||||
- identicon生成を無効にしてパフォーマンスを向上させることができるようになりました
 | 
			
		||||
- サーバーのマシン情報の公開を無効にしてパフォーマンスを向上させることができるようになりました
 | 
			
		||||
- 招待機能を改善しました
 | 
			
		||||
  * 過去に発行した招待コードを確認できるようになりました  
 | 
			
		||||
  * ロールごとに招待コードの発行数制限と制限対象期間、有効期限を設定できるようになりました  
 | 
			
		||||
  * 招待コードを作成したユーザーと使用したユーザーを確認できるようになりました  
 | 
			
		||||
- ユーザーにロールが期限付きでアサインされている場合、その期限をユーザーのモデレーションページで確認できるようになりました
 | 
			
		||||
- identicon生成を無効にしてパフォーマンスを向上させることができるようになりました
 | 
			
		||||
- サーバーのマシン情報の公開を無効にしてパフォーマンスを向上させることができるようになりました
 | 
			
		||||
 | 
			
		||||
### Client
 | 
			
		||||
- deck UIのカラムのメニューからアンテナとリストの編集画面を開けるように
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -220,14 +220,19 @@ export class RoleService implements OnApplicationShutdown {
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	@bindThis
 | 
			
		||||
	public async getUserRoles(userId: User['id']) {
 | 
			
		||||
	public async getUserAssigns(userId: User['id']) {
 | 
			
		||||
		const now = Date.now();
 | 
			
		||||
		let assigns = await this.roleAssignmentByUserIdCache.fetch(userId, () => this.roleAssignmentsRepository.findBy({ userId }));
 | 
			
		||||
		// 期限切れのロールを除外
 | 
			
		||||
		assigns = assigns.filter(a => a.expiresAt == null || (a.expiresAt.getTime() > now));
 | 
			
		||||
		const assignedRoleIds = assigns.map(x => x.roleId);
 | 
			
		||||
		return assigns;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@bindThis
 | 
			
		||||
	public async getUserRoles(userId: User['id']) {
 | 
			
		||||
		const roles = await this.rolesCache.fetch(() => this.rolesRepository.findBy({}));
 | 
			
		||||
		const assignedRoles = roles.filter(r => assignedRoleIds.includes(r.id));
 | 
			
		||||
		const assigns = await this.getUserAssigns(userId);
 | 
			
		||||
		const assignedRoles = roles.filter(r => assigns.map(x => x.roleId).includes(r.id));
 | 
			
		||||
		const user = roles.some(r => r.target === 'conditional') ? await this.cacheService.findUserById(userId) : null;
 | 
			
		||||
		const matchedCondRoles = roles.filter(r => r.target === 'conditional' && this.evalCond(user!, r.condFormula));
 | 
			
		||||
		return [...assignedRoles, ...matchedCondRoles];
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -61,6 +61,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
 | 
			
		|||
 | 
			
		||||
			const signins = await this.signinsRepository.findBy({ userId: user.id });
 | 
			
		||||
 | 
			
		||||
			const roleAssigns = await this.roleService.getUserAssigns(user.id);
 | 
			
		||||
			const roles = await this.roleService.getUserRoles(user.id);
 | 
			
		||||
 | 
			
		||||
			return {
 | 
			
		||||
| 
						 | 
				
			
			@ -85,6 +86,11 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
 | 
			
		|||
				signins,
 | 
			
		||||
				policies: await this.roleService.getUserPolicies(user.id),
 | 
			
		||||
				roles: await this.roleEntityService.packMany(roles, me),
 | 
			
		||||
				roleAssigns: roleAssigns.map(a => ({
 | 
			
		||||
					createdAt: a.createdAt.toISOString(),
 | 
			
		||||
					expiresAt: a.expiresAt ? a.expiresAt.toISOString() : null,
 | 
			
		||||
					roleId: a.roleId,
 | 
			
		||||
				})),
 | 
			
		||||
			};
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -40,7 +40,7 @@
 | 
			
		|||
										</div>
 | 
			
		||||
										<div v-if="expandedItems.includes(item.id)" :class="$style.userItemSub">
 | 
			
		||||
											<div>Assigned: <MkTime :time="item.createdAt" mode="detail"/></div>
 | 
			
		||||
											<div v-if="item.expiresAt">Period: {{ item.expiresAt.toLocaleString() }}</div>
 | 
			
		||||
											<div v-if="item.expiresAt">Period: {{ new Date(item.expiresAt).toLocaleString() }}</div>
 | 
			
		||||
											<div v-else>Period: {{ i18n.ts.indefinitely }}</div>
 | 
			
		||||
										</div>
 | 
			
		||||
									</div>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -55,7 +55,7 @@
 | 
			
		|||
						</div>
 | 
			
		||||
						<div v-if="expandedMuteItems.includes(item.id)" :class="$style.userItemSub">
 | 
			
		||||
							<div>Muted at: <MkTime :time="item.createdAt" mode="detail"/></div>
 | 
			
		||||
							<div v-if="item.expiresAt">Period: {{ item.expiresAt.toLocaleString() }}</div>
 | 
			
		||||
							<div v-if="item.expiresAt">Period: {{ new Date(item.expiresAt).toLocaleString() }}</div>
 | 
			
		||||
							<div v-else>Period: {{ i18n.ts.indefinitely }}</div>
 | 
			
		||||
						</div>
 | 
			
		||||
					</div>
 | 
			
		||||
| 
						 | 
				
			
			@ -85,7 +85,7 @@
 | 
			
		|||
						</div>
 | 
			
		||||
						<div v-if="expandedBlockItems.includes(item.id)" :class="$style.userItemSub">
 | 
			
		||||
							<div>Blocked at: <MkTime :time="item.createdAt" mode="detail"/></div>
 | 
			
		||||
							<div v-if="item.expiresAt">Period: {{ item.expiresAt.toLocaleString() }}</div>
 | 
			
		||||
							<div v-if="item.expiresAt">Period: {{ new Date(item.expiresAt).toLocaleString() }}</div>
 | 
			
		||||
							<div v-else>Period: {{ i18n.ts.indefinitely }}</div>
 | 
			
		||||
						</div>
 | 
			
		||||
					</div>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -112,9 +112,17 @@
 | 
			
		|||
						<MkButton v-if="user.host == null && iAmModerator" primary rounded @click="assignRole"><i class="ti ti-plus"></i> {{ i18n.ts.assign }}</MkButton>
 | 
			
		||||
 | 
			
		||||
						<div v-for="role in info.roles" :key="role.id" :class="$style.roleItem">
 | 
			
		||||
							<MkRolePreview :class="$style.role" :role="role" :forModeration="true"/>
 | 
			
		||||
							<button v-if="role.target === 'manual'" class="_button" :class="$style.roleUnassign" @click="unassignRole(role, $event)"><i class="ti ti-x"></i></button>
 | 
			
		||||
							<button v-else class="_button" :class="$style.roleUnassign" disabled><i class="ti ti-ban"></i></button>
 | 
			
		||||
							<div :class="$style.roleItemMain">
 | 
			
		||||
								<MkRolePreview :class="$style.role" :role="role" :forModeration="true"/>
 | 
			
		||||
								<button class="_button" :class="$style.roleToggle" @click="toggleRoleItem(role)"><i class="ti ti-chevron-down"></i></button>
 | 
			
		||||
								<button v-if="role.target === 'manual'" class="_button" :class="$style.roleUnassign" @click="unassignRole(role, $event)"><i class="ti ti-x"></i></button>
 | 
			
		||||
								<button v-else class="_button" :class="$style.roleUnassign" disabled><i class="ti ti-ban"></i></button>
 | 
			
		||||
							</div>
 | 
			
		||||
							<div v-if="expandedRoles.includes(role.id)" :class="$style.roleItemSub">
 | 
			
		||||
								<div>Assigned: <MkTime :time="info.roleAssigns.find(a => a.roleId === role.id).createdAt" mode="detail"/></div>
 | 
			
		||||
								<div v-if="info.roleAssigns.find(a => a.roleId === role.id).expiresAt">Period: {{ new Date(info.roleAssigns.find(a => a.roleId === role.id).expiresAt).toLocaleString() }}</div>
 | 
			
		||||
								<div v-else>Period: {{ i18n.ts.indefinitely }}</div>
 | 
			
		||||
							</div>
 | 
			
		||||
						</div>
 | 
			
		||||
					</div>
 | 
			
		||||
				</MkFolder>
 | 
			
		||||
| 
						 | 
				
			
			@ -220,6 +228,7 @@ const filesPagination = {
 | 
			
		|||
		userId: props.userId,
 | 
			
		||||
	})),
 | 
			
		||||
};
 | 
			
		||||
let expandedRoles = $ref([]);
 | 
			
		||||
 | 
			
		||||
function createFetcher() {
 | 
			
		||||
	if (iAmModerator) {
 | 
			
		||||
| 
						 | 
				
			
			@ -384,6 +393,14 @@ async function unassignRole(role, ev) {
 | 
			
		|||
	}], ev.currentTarget ?? ev.target);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function toggleRoleItem(role) {
 | 
			
		||||
	if (expandedRoles.includes(role.id)) {
 | 
			
		||||
		expandedRoles = expandedRoles.filter(x => x !== role.id);
 | 
			
		||||
	} else {
 | 
			
		||||
		expandedRoles.push(role.id);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
watch(() => props.userId, () => {
 | 
			
		||||
	init = createFetcher();
 | 
			
		||||
}, {
 | 
			
		||||
| 
						 | 
				
			
			@ -523,11 +540,22 @@ definePageMetadata(computed(() => ({
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
.roleItem {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.roleItemMain {
 | 
			
		||||
	display: flex;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.role {
 | 
			
		||||
	flex: 1;
 | 
			
		||||
	min-width: 0;
 | 
			
		||||
	margin-right: 8px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.roleItemSub {
 | 
			
		||||
	padding: 6px 12px;
 | 
			
		||||
	font-size: 85%;
 | 
			
		||||
	color: var(--fgTransparentWeak);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.roleUnassign {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue