feat(client): add user list widget
This commit is contained in:
		
							parent
							
								
									c7350c3839
								
							
						
					
					
						commit
						6acc10b4ba
					
				
					 4 changed files with 142 additions and 0 deletions
				
			
		|  | @ -42,6 +42,7 @@ You should also include the user name that made the change. | ||||||
| - Client: Implement the button to subscribe push notification @tamaina | - Client: Implement the button to subscribe push notification @tamaina | ||||||
| - Client: Implement the toggle to or not to close push notifications when notifications or messages are read @tamaina | - Client: Implement the toggle to or not to close push notifications when notifications or messages are read @tamaina | ||||||
| - Client: show Unicode emoji tooltip with its name in MkReactionsViewer.reaction @saschanaz | - Client: show Unicode emoji tooltip with its name in MkReactionsViewer.reaction @saschanaz | ||||||
|  | - Client: add user list widget @syuilo | ||||||
| - Client: improve overall performance of client @syuilo | - Client: improve overall performance of client @syuilo | ||||||
| 
 | 
 | ||||||
| ### Bugfixes | ### Bugfixes | ||||||
|  |  | ||||||
|  | @ -1344,6 +1344,9 @@ _widgets: | ||||||
|   serverMetric: "サーバーメトリクス" |   serverMetric: "サーバーメトリクス" | ||||||
|   aiscript: "AiScriptコンソール" |   aiscript: "AiScriptコンソール" | ||||||
|   aichan: "藍" |   aichan: "藍" | ||||||
|  |   userList: "ユーザーリスト" | ||||||
|  |   _userList: | ||||||
|  |     chooseList: "リストを選択" | ||||||
| 
 | 
 | ||||||
| _cw: | _cw: | ||||||
|   hide: "隠す" |   hide: "隠す" | ||||||
|  |  | ||||||
|  | @ -23,6 +23,7 @@ export default function(app: App) { | ||||||
| 	app.component('MkwButton', defineAsyncComponent(() => import('./button.vue'))); | 	app.component('MkwButton', defineAsyncComponent(() => import('./button.vue'))); | ||||||
| 	app.component('MkwAiscript', defineAsyncComponent(() => import('./aiscript.vue'))); | 	app.component('MkwAiscript', defineAsyncComponent(() => import('./aiscript.vue'))); | ||||||
| 	app.component('MkwAichan', defineAsyncComponent(() => import('./aichan.vue'))); | 	app.component('MkwAichan', defineAsyncComponent(() => import('./aichan.vue'))); | ||||||
|  | 	app.component('MkwUserList', defineAsyncComponent(() => import('./user-list.vue'))); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export const widgets = [ | export const widgets = [ | ||||||
|  | @ -48,4 +49,5 @@ export const widgets = [ | ||||||
| 	'button', | 	'button', | ||||||
| 	'aiscript', | 	'aiscript', | ||||||
| 	'aichan', | 	'aichan', | ||||||
|  | 	'userList', | ||||||
| ]; | ]; | ||||||
|  |  | ||||||
							
								
								
									
										136
									
								
								packages/client/src/widgets/user-list.vue
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										136
									
								
								packages/client/src/widgets/user-list.vue
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,136 @@ | ||||||
|  | <template> | ||||||
|  | <MkContainer :show-header="widgetProps.showHeader" class="mkw-userList"> | ||||||
|  | 	<template #header><i class="ti ti-users"></i>{{ list ? list.name : i18n.ts._widgets.userList }}</template> | ||||||
|  | 	<template #func><button class="_button" @click="configure()"><i class="ti ti-settings"></i></button></template> | ||||||
|  | 
 | ||||||
|  | 	<div :class="$style.root"> | ||||||
|  | 		<div v-if="widgetProps.listId == null" class="init"> | ||||||
|  | 			<MkButton primary @click="chooseList">{{ i18n.ts._widgets._userList.chooseList }}</MkButton> | ||||||
|  | 		</div> | ||||||
|  | 		<MkLoading v-else-if="fetching"/> | ||||||
|  | 		<div v-else class="users"> | ||||||
|  | 			<MkA v-for="user in users" :key="user.id" class="user"> | ||||||
|  | 				<MkAvatar :user="user" class="avatar" :show-indicator="true"/> | ||||||
|  | 			</MkA> | ||||||
|  | 		</div> | ||||||
|  | 	</div> | ||||||
|  | </MkContainer> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script lang="ts" setup> | ||||||
|  | import { onMounted, onUnmounted, ref } from 'vue'; | ||||||
|  | import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget'; | ||||||
|  | import { GetFormResultType } from '@/scripts/form'; | ||||||
|  | import MkContainer from '@/components/MkContainer.vue'; | ||||||
|  | import * as os from '@/os'; | ||||||
|  | import { useInterval } from '@/scripts/use-interval'; | ||||||
|  | import { i18n } from '@/i18n'; | ||||||
|  | import MkButton from '@/components/MkButton.vue'; | ||||||
|  | 
 | ||||||
|  | const name = 'userList'; | ||||||
|  | 
 | ||||||
|  | const widgetPropsDef = { | ||||||
|  | 	showHeader: { | ||||||
|  | 		type: 'boolean' as const, | ||||||
|  | 		default: true, | ||||||
|  | 	}, | ||||||
|  | 	listId: { | ||||||
|  | 		type: 'string' as const, | ||||||
|  | 		default: null, | ||||||
|  | 		hidden: true, | ||||||
|  | 	}, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | type WidgetProps = GetFormResultType<typeof widgetPropsDef>; | ||||||
|  | 
 | ||||||
|  | // 現時点ではvueの制限によりimportしたtypeをジェネリックに渡せない | ||||||
|  | //const props = defineProps<WidgetComponentProps<WidgetProps>>(); | ||||||
|  | //const emit = defineEmits<WidgetComponentEmits<WidgetProps>>(); | ||||||
|  | const props = defineProps<{ widget?: Widget<WidgetProps>; }>(); | ||||||
|  | const emit = defineEmits<{ (ev: 'updateProps', props: WidgetProps); }>(); | ||||||
|  | 
 | ||||||
|  | const { widgetProps, configure, save } = useWidgetPropsManager(name, | ||||||
|  | 	widgetPropsDef, | ||||||
|  | 	props, | ||||||
|  | 	emit, | ||||||
|  | ); | ||||||
|  | 
 | ||||||
|  | let list = $ref(); | ||||||
|  | let users = $ref([]); | ||||||
|  | let fetching = $ref(true); | ||||||
|  | 
 | ||||||
|  | async function chooseList() { | ||||||
|  | 	const lists = await os.api('users/lists/list'); | ||||||
|  | 	const { canceled, result: list } = await os.select({ | ||||||
|  | 		title: i18n.ts.selectList, | ||||||
|  | 		items: lists.map(x => ({ | ||||||
|  | 			value: x, text: x.name, | ||||||
|  | 		})), | ||||||
|  | 		default: widgetProps.listId, | ||||||
|  | 	}); | ||||||
|  | 	if (canceled) return; | ||||||
|  | 
 | ||||||
|  | 	widgetProps.listId = list.id; | ||||||
|  | 	save(); | ||||||
|  | 	fetch(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const fetch = () => { | ||||||
|  | 	if (widgetProps.listId == null) { | ||||||
|  | 		fetching = false; | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	os.api('users/lists/show', { | ||||||
|  | 		listId: widgetProps.listId, | ||||||
|  | 	}).then(_list => { | ||||||
|  | 		list = _list; | ||||||
|  | 		os.api('users/show', { | ||||||
|  | 			userIds: list.userIds, | ||||||
|  | 		}).then(_users => { | ||||||
|  | 			users = _users; | ||||||
|  | 			fetching = false; | ||||||
|  | 		}); | ||||||
|  | 	}); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | useInterval(fetch, 1000 * 60, { | ||||||
|  | 	immediate: true, | ||||||
|  | 	afterMounted: true, | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | defineExpose<WidgetComponentExpose>({ | ||||||
|  | 	name, | ||||||
|  | 	configure, | ||||||
|  | 	id: props.widget ? props.widget.id : null, | ||||||
|  | }); | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <style lang="scss" module> | ||||||
|  | .root { | ||||||
|  | 	&:global { | ||||||
|  | 		> .init { | ||||||
|  | 			padding: 16px; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		> .users { | ||||||
|  | 			display: grid; | ||||||
|  | 			grid-template-columns: repeat(auto-fill, minmax(30px, 40px)); | ||||||
|  | 			grid-gap: 12px; | ||||||
|  | 			place-content: center; | ||||||
|  | 			padding: 16px; | ||||||
|  | 
 | ||||||
|  | 			> .user { | ||||||
|  | 				width: 100%; | ||||||
|  | 				height: 100%; | ||||||
|  | 				aspect-ratio: 1; | ||||||
|  | 
 | ||||||
|  | 				> .avatar { | ||||||
|  | 					width: 100%; | ||||||
|  | 					height: 100%; | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | </style> | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue