parent
							
								
									1a698111a4
								
							
						
					
					
						commit
						0f1c0a42a2
					
				
					 15 changed files with 190 additions and 300 deletions
				
			
		|  | @ -857,6 +857,7 @@ check: "チェック" | ||||||
| isSystemAccount: "システムにより自動で作成・管理されているアカウントです。" | isSystemAccount: "システムにより自動で作成・管理されているアカウントです。" | ||||||
| typeToConfirm: "この操作を行うには {x} と入力してください" | typeToConfirm: "この操作を行うには {x} と入力してください" | ||||||
| deleteAccount: "アカウント削除" | deleteAccount: "アカウント削除" | ||||||
|  | document: "ドキュメント" | ||||||
| 
 | 
 | ||||||
| _emailUnavailable: | _emailUnavailable: | ||||||
|   used: "既に使用されています" |   used: "既に使用されています" | ||||||
|  |  | ||||||
|  | @ -16,13 +16,13 @@ | ||||||
| 			</template> | 			</template> | ||||||
| 		</div> | 		</div> | ||||||
| 		<div class="sub"> | 		<div class="sub"> | ||||||
| 			<a v-click-anime href="https://misskey-hub.net/help.html" target="_blank" @click.passive="close()"> | 			<button v-click-anime class="_button" @click="help"> | ||||||
| 				<i class="fas fa-question-circle icon"></i> | 				<i class="fas fa-question-circle icon"></i> | ||||||
| 				<div class="text">{{ $ts.help }}</div> | 				<div class="text">{{ $ts.help }}</div> | ||||||
| 			</a> | 			</button> | ||||||
| 			<MkA v-click-anime to="/about" @click.passive="close()"> | 			<MkA v-click-anime to="/about" @click.passive="close()"> | ||||||
| 				<i class="fas fa-info-circle icon"></i> | 				<i class="fas fa-info-circle icon"></i> | ||||||
| 				<div class="text">{{ $t('aboutX', { x: instanceName }) }}</div> | 				<div class="text">{{ $ts.instanceInfo }}</div> | ||||||
| 			</MkA> | 			</MkA> | ||||||
| 			<MkA v-click-anime to="/about-misskey" @click.passive="close()"> | 			<MkA v-click-anime to="/about-misskey" @click.passive="close()"> | ||||||
| 				<img src="/static-assets/favicon.png" class="icon"/> | 				<img src="/static-assets/favicon.png" class="icon"/> | ||||||
|  | @ -34,13 +34,14 @@ | ||||||
| </template> | </template> | ||||||
| 
 | 
 | ||||||
| <script lang="ts" setup> | <script lang="ts" setup> | ||||||
| import {  } from 'vue'; | import { } from 'vue'; | ||||||
| import MkModal from '@/components/ui/modal.vue'; | import MkModal from '@/components/ui/modal.vue'; | ||||||
| import { menuDef } from '@/menu'; | import { menuDef } from '@/menu'; | ||||||
| import { instanceName } from '@/config'; | import { instanceName } from '@/config'; | ||||||
| import { defaultStore } from '@/store'; | import { defaultStore } from '@/store'; | ||||||
| import { i18n } from '@/i18n'; | import { i18n } from '@/i18n'; | ||||||
| import { deviceKind } from '@/scripts/device-kind'; | import { deviceKind } from '@/scripts/device-kind'; | ||||||
|  | import * as os from '@/os'; | ||||||
| 
 | 
 | ||||||
| const props = withDefaults(defineProps<{ | const props = withDefaults(defineProps<{ | ||||||
| 	src?: HTMLElement; | 	src?: HTMLElement; | ||||||
|  | @ -73,6 +74,28 @@ const items = Object.keys(menuDef).filter(k => !menu.includes(k)).map(k => menuD | ||||||
| function close() { | function close() { | ||||||
| 	modal.close(); | 	modal.close(); | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | function help(ev: MouseEvent) { | ||||||
|  | 	os.popupMenu([{ | ||||||
|  | 		type: 'link', | ||||||
|  | 		to: '/mfm-cheat-sheet', | ||||||
|  | 		text: i18n.ts._mfm.cheatSheet, | ||||||
|  | 		icon: 'fas fa-code', | ||||||
|  | 	}, { | ||||||
|  | 		type: 'link', | ||||||
|  | 		to: '/scratchpad', | ||||||
|  | 		text: i18n.ts.scratchpad, | ||||||
|  | 		icon: 'fas fa-terminal', | ||||||
|  | 	}, null, { | ||||||
|  | 		text: i18n.ts.document, | ||||||
|  | 		icon: 'fas fa-question-circle', | ||||||
|  | 		action: () => { | ||||||
|  | 			window.open('https://misskey-hub.net/help.html', '_blank'); | ||||||
|  | 		}, | ||||||
|  | 	}], ev.currentTarget ?? ev.target); | ||||||
|  | 
 | ||||||
|  | 	close(); | ||||||
|  | } | ||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
| <style lang="scss" scoped> | <style lang="scss" scoped> | ||||||
|  |  | ||||||
|  | @ -112,20 +112,6 @@ export const menuDef = reactive({ | ||||||
| 			os.popupMenu(items, ev.currentTarget ?? ev.target); | 			os.popupMenu(items, ev.currentTarget ?? ev.target); | ||||||
| 		}, | 		}, | ||||||
| 	}, | 	}, | ||||||
| 	mentions: { |  | ||||||
| 		title: 'mentions', |  | ||||||
| 		icon: 'fas fa-at', |  | ||||||
| 		show: computed(() => $i != null), |  | ||||||
| 		indicated: computed(() => $i != null && $i.hasUnreadMentions), |  | ||||||
| 		to: '/my/mentions', |  | ||||||
| 	}, |  | ||||||
| 	messages: { |  | ||||||
| 		title: 'directNotes', |  | ||||||
| 		icon: 'fas fa-envelope', |  | ||||||
| 		show: computed(() => $i != null), |  | ||||||
| 		indicated: computed(() => $i != null && $i.hasUnreadSpecifiedNotes), |  | ||||||
| 		to: '/my/messages', |  | ||||||
| 	}, |  | ||||||
| 	favorites: { | 	favorites: { | ||||||
| 		title: 'favorites', | 		title: 'favorites', | ||||||
| 		icon: 'fas fa-star', | 		icon: 'fas fa-star', | ||||||
|  | @ -153,21 +139,6 @@ export const menuDef = reactive({ | ||||||
| 		icon: 'fas fa-satellite-dish', | 		icon: 'fas fa-satellite-dish', | ||||||
| 		to: '/channels', | 		to: '/channels', | ||||||
| 	}, | 	}, | ||||||
| 	federation: { |  | ||||||
| 		title: 'federation', |  | ||||||
| 		icon: 'fas fa-globe', |  | ||||||
| 		to: '/federation', |  | ||||||
| 	}, |  | ||||||
| 	emojis: { |  | ||||||
| 		title: 'emojis', |  | ||||||
| 		icon: 'fas fa-laugh', |  | ||||||
| 		to: '/emojis', |  | ||||||
| 	}, |  | ||||||
| 	scratchpad: { |  | ||||||
| 		title: 'scratchpad', |  | ||||||
| 		icon: 'fas fa-terminal', |  | ||||||
| 		to: '/scratchpad', |  | ||||||
| 	}, |  | ||||||
| 	ui: { | 	ui: { | ||||||
| 		title: 'switchUi', | 		title: 'switchUi', | ||||||
| 		icon: 'fas fa-columns', | 		icon: 'fas fa-columns', | ||||||
|  |  | ||||||
							
								
								
									
										106
									
								
								packages/client/src/pages/about.federation.vue
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								packages/client/src/pages/about.federation.vue
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,106 @@ | ||||||
|  | <template> | ||||||
|  | <div class="taeiyria"> | ||||||
|  | 	<div class="query"> | ||||||
|  | 		<MkInput v-model="host" :debounce="true" class=""> | ||||||
|  | 			<template #prefix><i class="fas fa-search"></i></template> | ||||||
|  | 			<template #label>{{ $ts.host }}</template> | ||||||
|  | 		</MkInput> | ||||||
|  | 		<FormSplit style="margin-top: var(--margin);"> | ||||||
|  | 			<MkSelect v-model="state"> | ||||||
|  | 				<template #label>{{ $ts.state }}</template> | ||||||
|  | 				<option value="all">{{ $ts.all }}</option> | ||||||
|  | 				<option value="federating">{{ $ts.federating }}</option> | ||||||
|  | 				<option value="subscribing">{{ $ts.subscribing }}</option> | ||||||
|  | 				<option value="publishing">{{ $ts.publishing }}</option> | ||||||
|  | 				<option value="suspended">{{ $ts.suspended }}</option> | ||||||
|  | 				<option value="blocked">{{ $ts.blocked }}</option> | ||||||
|  | 				<option value="notResponding">{{ $ts.notResponding }}</option> | ||||||
|  | 			</MkSelect> | ||||||
|  | 			<MkSelect v-model="sort"> | ||||||
|  | 				<template #label>{{ $ts.sort }}</template> | ||||||
|  | 				<option value="+pubSub">{{ $ts.pubSub }} ({{ $ts.descendingOrder }})</option> | ||||||
|  | 				<option value="-pubSub">{{ $ts.pubSub }} ({{ $ts.ascendingOrder }})</option> | ||||||
|  | 				<option value="+notes">{{ $ts.notes }} ({{ $ts.descendingOrder }})</option> | ||||||
|  | 				<option value="-notes">{{ $ts.notes }} ({{ $ts.ascendingOrder }})</option> | ||||||
|  | 				<option value="+users">{{ $ts.users }} ({{ $ts.descendingOrder }})</option> | ||||||
|  | 				<option value="-users">{{ $ts.users }} ({{ $ts.ascendingOrder }})</option> | ||||||
|  | 				<option value="+following">{{ $ts.following }} ({{ $ts.descendingOrder }})</option> | ||||||
|  | 				<option value="-following">{{ $ts.following }} ({{ $ts.ascendingOrder }})</option> | ||||||
|  | 				<option value="+followers">{{ $ts.followers }} ({{ $ts.descendingOrder }})</option> | ||||||
|  | 				<option value="-followers">{{ $ts.followers }} ({{ $ts.ascendingOrder }})</option> | ||||||
|  | 				<option value="+caughtAt">{{ $ts.registeredAt }} ({{ $ts.descendingOrder }})</option> | ||||||
|  | 				<option value="-caughtAt">{{ $ts.registeredAt }} ({{ $ts.ascendingOrder }})</option> | ||||||
|  | 				<option value="+lastCommunicatedAt">{{ $ts.lastCommunication }} ({{ $ts.descendingOrder }})</option> | ||||||
|  | 				<option value="-lastCommunicatedAt">{{ $ts.lastCommunication }} ({{ $ts.ascendingOrder }})</option> | ||||||
|  | 			</MkSelect> | ||||||
|  | 		</FormSplit> | ||||||
|  | 	</div> | ||||||
|  | 
 | ||||||
|  | 	<MkPagination v-slot="{items}" ref="instances" :key="host + state" :pagination="pagination"> | ||||||
|  | 		<div class="dqokceoi"> | ||||||
|  | 			<MkA v-for="instance in items" :key="instance.id" v-tooltip.mfm="`Last communicated: ${new Date(instance.lastCommunicatedAt).toLocaleString()}\nStatus: ${getStatus(instance)}`" class="instance" :to="`/instance-info/${instance.host}`"> | ||||||
|  | 				<MkInstanceCardMini :instance="instance"/> | ||||||
|  | 			</MkA> | ||||||
|  | 		</div> | ||||||
|  | 	</MkPagination> | ||||||
|  | </div> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script lang="ts" setup> | ||||||
|  | import { computed } from 'vue'; | ||||||
|  | import MkButton from '@/components/ui/button.vue'; | ||||||
|  | import MkInput from '@/components/form/input.vue'; | ||||||
|  | import MkSelect from '@/components/form/select.vue'; | ||||||
|  | import MkPagination from '@/components/ui/pagination.vue'; | ||||||
|  | import MkInstanceCardMini from '@/components/instance-card-mini.vue'; | ||||||
|  | import FormSplit from '@/components/form/split.vue'; | ||||||
|  | import * as os from '@/os'; | ||||||
|  | import { i18n } from '@/i18n'; | ||||||
|  | 
 | ||||||
|  | let host = $ref(''); | ||||||
|  | let state = $ref('federating'); | ||||||
|  | let sort = $ref('+pubSub'); | ||||||
|  | const pagination = { | ||||||
|  | 	endpoint: 'federation/instances' as const, | ||||||
|  | 	limit: 10, | ||||||
|  | 	offsetMode: true, | ||||||
|  | 	params: computed(() => ({ | ||||||
|  | 		sort: sort, | ||||||
|  | 		host: host !== '' ? host : null, | ||||||
|  | 		...( | ||||||
|  | 			state === 'federating' ? { federating: true } : | ||||||
|  | 			state === 'subscribing' ? { subscribing: true } : | ||||||
|  | 			state === 'publishing' ? { publishing: true } : | ||||||
|  | 			state === 'suspended' ? { suspended: true } : | ||||||
|  | 			state === 'blocked' ? { blocked: true } : | ||||||
|  | 			state === 'notResponding' ? { notResponding: true } : | ||||||
|  | 			{}), | ||||||
|  | 	})), | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | function getStatus(instance) { | ||||||
|  | 	if (instance.isSuspended) return 'Suspended'; | ||||||
|  | 	if (instance.isBlocked) return 'Blocked'; | ||||||
|  | 	if (instance.isNotResponding) return 'Error'; | ||||||
|  | 	return 'Alive'; | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <style lang="scss" scoped> | ||||||
|  | .taeiyria { | ||||||
|  | 	> .query { | ||||||
|  | 		background: var(--bg); | ||||||
|  | 		margin-bottom: 16px; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .dqokceoi { | ||||||
|  | 	display: grid; | ||||||
|  | 	grid-template-columns: repeat(auto-fill, minmax(270px, 1fr)); | ||||||
|  | 	grid-gap: 12px; | ||||||
|  | 
 | ||||||
|  | 	> .instance:hover { | ||||||
|  | 		text-decoration: none; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | </style> | ||||||
|  | @ -67,6 +67,12 @@ | ||||||
| 			</FormSection> | 			</FormSection> | ||||||
| 		</div> | 		</div> | ||||||
| 	</MkSpacer> | 	</MkSpacer> | ||||||
|  | 	<MkSpacer v-else-if="tab === 'emojis'" :content-max="1000" :margin-min="20"> | ||||||
|  | 		<XEmojis/> | ||||||
|  | 	</MkSpacer> | ||||||
|  | 	<MkSpacer v-else-if="tab === 'federation'" :content-max="1000" :margin-min="20"> | ||||||
|  | 		<XFederation/> | ||||||
|  | 	</MkSpacer> | ||||||
| 	<MkSpacer v-else-if="tab === 'charts'" :content-max="1200" :margin-min="20"> | 	<MkSpacer v-else-if="tab === 'charts'" :content-max="1200" :margin-min="20"> | ||||||
| 		<MkInstanceStats :chart-limit="500" :detailed="true"/> | 		<MkInstanceStats :chart-limit="500" :detailed="true"/> | ||||||
| 	</MkSpacer> | 	</MkSpacer> | ||||||
|  | @ -75,6 +81,8 @@ | ||||||
| 
 | 
 | ||||||
| <script lang="ts" setup> | <script lang="ts" setup> | ||||||
| import { ref, computed } from 'vue'; | import { ref, computed } from 'vue'; | ||||||
|  | import XEmojis from './about.emojis.vue'; | ||||||
|  | import XFederation from './about.federation.vue'; | ||||||
| import { version, instanceName , host } from '@/config'; | import { version, instanceName , host } from '@/config'; | ||||||
| import FormLink from '@/components/form/link.vue'; | import FormLink from '@/components/form/link.vue'; | ||||||
| import FormSection from '@/components/form/section.vue'; | import FormSection from '@/components/form/section.vue'; | ||||||
|  | @ -100,10 +108,18 @@ const headerActions = $computed(() => []); | ||||||
| const headerTabs = $computed(() => [{ | const headerTabs = $computed(() => [{ | ||||||
| 	key: 'overview', | 	key: 'overview', | ||||||
| 	title: i18n.ts.overview, | 	title: i18n.ts.overview, | ||||||
|  | }, { | ||||||
|  | 	key: 'emojis', | ||||||
|  | 	title: i18n.ts.customEmojis, | ||||||
|  | 	icon: 'fas fa-laugh', | ||||||
|  | }, { | ||||||
|  | 	key: 'federation', | ||||||
|  | 	title: i18n.ts.federation, | ||||||
|  | 	icon: 'fas fa-globe', | ||||||
| }, { | }, { | ||||||
| 	key: 'charts', | 	key: 'charts', | ||||||
| 	title: i18n.ts.charts, | 	title: i18n.ts.charts, | ||||||
| 	icon: 'fas fa-chart-bar', | 	icon: 'fas fa-chart-simple', | ||||||
| }]); | }]); | ||||||
| 
 | 
 | ||||||
| definePageMetadata(computed(() => ({ | definePageMetadata(computed(() => ({ | ||||||
|  |  | ||||||
|  | @ -201,7 +201,7 @@ const component = $computed(() => { | ||||||
| 		case 'overview': return defineAsyncComponent(() => import('./overview.vue')); | 		case 'overview': return defineAsyncComponent(() => import('./overview.vue')); | ||||||
| 		case 'users': return defineAsyncComponent(() => import('./users.vue')); | 		case 'users': return defineAsyncComponent(() => import('./users.vue')); | ||||||
| 		case 'emojis': return defineAsyncComponent(() => import('./emojis.vue')); | 		case 'emojis': return defineAsyncComponent(() => import('./emojis.vue')); | ||||||
| 		case 'federation': return defineAsyncComponent(() => import('../federation.vue')); | 		//case 'federation': return defineAsyncComponent(() => import('../federation.vue')); | ||||||
| 		case 'queue': return defineAsyncComponent(() => import('./queue.vue')); | 		case 'queue': return defineAsyncComponent(() => import('./queue.vue')); | ||||||
| 		case 'files': return defineAsyncComponent(() => import('./files.vue')); | 		case 'files': return defineAsyncComponent(() => import('./files.vue')); | ||||||
| 		case 'announcements': return defineAsyncComponent(() => import('./announcements.vue')); | 		case 'announcements': return defineAsyncComponent(() => import('./announcements.vue')); | ||||||
|  |  | ||||||
|  | @ -1,60 +0,0 @@ | ||||||
| <template> |  | ||||||
| <MkStickyContainer> |  | ||||||
| 	<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template> |  | ||||||
| 	<div :class="$style.root"> |  | ||||||
| 		<XCategory v-if="tab === 'category'"/> |  | ||||||
| 	</div> |  | ||||||
| </MkStickyContainer> |  | ||||||
| </template> |  | ||||||
| 
 |  | ||||||
| <script lang="ts" setup> |  | ||||||
| import { ref, computed } from 'vue'; |  | ||||||
| import XCategory from './emojis.category.vue'; |  | ||||||
| import * as os from '@/os'; |  | ||||||
| import { i18n } from '@/i18n'; |  | ||||||
| import { definePageMetadata } from '@/scripts/page-metadata'; |  | ||||||
| 
 |  | ||||||
| const tab = ref('category'); |  | ||||||
| 
 |  | ||||||
| function menu(ev) { |  | ||||||
| 	os.popupMenu([{ |  | ||||||
| 		icon: 'fas fa-download', |  | ||||||
| 		text: i18n.ts.export, |  | ||||||
| 		action: async () => { |  | ||||||
| 			os.api('export-custom-emojis', { |  | ||||||
| 			}) |  | ||||||
| 			.then(() => { |  | ||||||
| 				os.alert({ |  | ||||||
| 					type: 'info', |  | ||||||
| 					text: i18n.ts.exportRequested, |  | ||||||
| 				}); |  | ||||||
| 			}).catch((err) => { |  | ||||||
| 				os.alert({ |  | ||||||
| 					type: 'error', |  | ||||||
| 					text: err.message, |  | ||||||
| 				}); |  | ||||||
| 			}); |  | ||||||
| 		}, |  | ||||||
| 	}], ev.currentTarget ?? ev.target); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| const headerActions = $computed(() => [{ |  | ||||||
| 	icon: 'fas fa-ellipsis-h', |  | ||||||
| 	handler: menu, |  | ||||||
| }]); |  | ||||||
| 
 |  | ||||||
| const headerTabs = $computed(() => []); |  | ||||||
| 
 |  | ||||||
| definePageMetadata({ |  | ||||||
| 	title: i18n.ts.customEmojis, |  | ||||||
| 	icon: 'fas fa-laugh', |  | ||||||
| 	bg: 'var(--bg)', |  | ||||||
| }); |  | ||||||
| </script> |  | ||||||
| 
 |  | ||||||
| <style lang="scss" module> |  | ||||||
| .root { |  | ||||||
| 	max-width: 1000px; |  | ||||||
| 	margin: 0 auto; |  | ||||||
| } |  | ||||||
| </style> |  | ||||||
|  | @ -1,122 +0,0 @@ | ||||||
| <template> |  | ||||||
| <MkStickyContainer> |  | ||||||
| 	<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template> |  | ||||||
| 	<MkSpacer :content-max="1000"> |  | ||||||
| 		<div class="taeiyria"> |  | ||||||
| 			<div class="query"> |  | ||||||
| 				<MkInput v-model="host" :debounce="true" class=""> |  | ||||||
| 					<template #prefix><i class="fas fa-search"></i></template> |  | ||||||
| 					<template #label>{{ $ts.host }}</template> |  | ||||||
| 				</MkInput> |  | ||||||
| 				<FormSplit style="margin-top: var(--margin);"> |  | ||||||
| 					<MkSelect v-model="state"> |  | ||||||
| 						<template #label>{{ $ts.state }}</template> |  | ||||||
| 						<option value="all">{{ $ts.all }}</option> |  | ||||||
| 						<option value="federating">{{ $ts.federating }}</option> |  | ||||||
| 						<option value="subscribing">{{ $ts.subscribing }}</option> |  | ||||||
| 						<option value="publishing">{{ $ts.publishing }}</option> |  | ||||||
| 						<option value="suspended">{{ $ts.suspended }}</option> |  | ||||||
| 						<option value="blocked">{{ $ts.blocked }}</option> |  | ||||||
| 						<option value="notResponding">{{ $ts.notResponding }}</option> |  | ||||||
| 					</MkSelect> |  | ||||||
| 					<MkSelect v-model="sort"> |  | ||||||
| 						<template #label>{{ $ts.sort }}</template> |  | ||||||
| 						<option value="+pubSub">{{ $ts.pubSub }} ({{ $ts.descendingOrder }})</option> |  | ||||||
| 						<option value="-pubSub">{{ $ts.pubSub }} ({{ $ts.ascendingOrder }})</option> |  | ||||||
| 						<option value="+notes">{{ $ts.notes }} ({{ $ts.descendingOrder }})</option> |  | ||||||
| 						<option value="-notes">{{ $ts.notes }} ({{ $ts.ascendingOrder }})</option> |  | ||||||
| 						<option value="+users">{{ $ts.users }} ({{ $ts.descendingOrder }})</option> |  | ||||||
| 						<option value="-users">{{ $ts.users }} ({{ $ts.ascendingOrder }})</option> |  | ||||||
| 						<option value="+following">{{ $ts.following }} ({{ $ts.descendingOrder }})</option> |  | ||||||
| 						<option value="-following">{{ $ts.following }} ({{ $ts.ascendingOrder }})</option> |  | ||||||
| 						<option value="+followers">{{ $ts.followers }} ({{ $ts.descendingOrder }})</option> |  | ||||||
| 						<option value="-followers">{{ $ts.followers }} ({{ $ts.ascendingOrder }})</option> |  | ||||||
| 						<option value="+caughtAt">{{ $ts.registeredAt }} ({{ $ts.descendingOrder }})</option> |  | ||||||
| 						<option value="-caughtAt">{{ $ts.registeredAt }} ({{ $ts.ascendingOrder }})</option> |  | ||||||
| 						<option value="+lastCommunicatedAt">{{ $ts.lastCommunication }} ({{ $ts.descendingOrder }})</option> |  | ||||||
| 						<option value="-lastCommunicatedAt">{{ $ts.lastCommunication }} ({{ $ts.ascendingOrder }})</option> |  | ||||||
| 					</MkSelect> |  | ||||||
| 				</FormSplit> |  | ||||||
| 			</div> |  | ||||||
| 
 |  | ||||||
| 			<MkPagination v-slot="{items}" ref="instances" :key="host + state" :pagination="pagination"> |  | ||||||
| 				<div class="dqokceoi"> |  | ||||||
| 					<MkA v-for="instance in items" :key="instance.id" v-tooltip.mfm="`Last communicated: ${new Date(instance.lastCommunicatedAt).toLocaleString()}\nStatus: ${getStatus(instance)}`" class="instance" :to="`/instance-info/${instance.host}`"> |  | ||||||
| 						<MkInstanceCardMini :instance="instance"/> |  | ||||||
| 					</MkA> |  | ||||||
| 				</div> |  | ||||||
| 			</MkPagination> |  | ||||||
| 		</div> |  | ||||||
| 	</MkSpacer> |  | ||||||
| </MkStickyContainer> |  | ||||||
| </template> |  | ||||||
| 
 |  | ||||||
| <script lang="ts" setup> |  | ||||||
| import { computed } from 'vue'; |  | ||||||
| import MkButton from '@/components/ui/button.vue'; |  | ||||||
| import MkInput from '@/components/form/input.vue'; |  | ||||||
| import MkSelect from '@/components/form/select.vue'; |  | ||||||
| import MkPagination from '@/components/ui/pagination.vue'; |  | ||||||
| import MkInstanceCardMini from '@/components/instance-card-mini.vue'; |  | ||||||
| import FormSplit from '@/components/form/split.vue'; |  | ||||||
| import * as os from '@/os'; |  | ||||||
| import { i18n } from '@/i18n'; |  | ||||||
| import { definePageMetadata } from '@/scripts/page-metadata'; |  | ||||||
| 
 |  | ||||||
| let host = $ref(''); |  | ||||||
| let state = $ref('federating'); |  | ||||||
| let sort = $ref('+pubSub'); |  | ||||||
| const pagination = { |  | ||||||
| 	endpoint: 'federation/instances' as const, |  | ||||||
| 	limit: 10, |  | ||||||
| 	offsetMode: true, |  | ||||||
| 	params: computed(() => ({ |  | ||||||
| 		sort: sort, |  | ||||||
| 		host: host !== '' ? host : null, |  | ||||||
| 		...( |  | ||||||
| 			state === 'federating' ? { federating: true } : |  | ||||||
| 			state === 'subscribing' ? { subscribing: true } : |  | ||||||
| 			state === 'publishing' ? { publishing: true } : |  | ||||||
| 			state === 'suspended' ? { suspended: true } : |  | ||||||
| 			state === 'blocked' ? { blocked: true } : |  | ||||||
| 			state === 'notResponding' ? { notResponding: true } : |  | ||||||
| 			{}), |  | ||||||
| 	})), |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| function getStatus(instance) { |  | ||||||
| 	if (instance.isSuspended) return 'Suspended'; |  | ||||||
| 	if (instance.isBlocked) return 'Blocked'; |  | ||||||
| 	if (instance.isNotResponding) return 'Error'; |  | ||||||
| 	return 'Alive'; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| const headerActions = $computed(() => []); |  | ||||||
| 
 |  | ||||||
| const headerTabs = $computed(() => []); |  | ||||||
| 
 |  | ||||||
| definePageMetadata({ |  | ||||||
| 	title: i18n.ts.federation, |  | ||||||
| 	icon: 'fas fa-globe', |  | ||||||
| 	bg: 'var(--bg)', |  | ||||||
| }); |  | ||||||
| </script> |  | ||||||
| 
 |  | ||||||
| <style lang="scss" scoped> |  | ||||||
| .taeiyria { |  | ||||||
| 	> .query { |  | ||||||
| 		background: var(--bg); |  | ||||||
| 		margin-bottom: 16px; |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| .dqokceoi { |  | ||||||
| 	display: grid; |  | ||||||
| 	grid-template-columns: repeat(auto-fill, minmax(270px, 1fr)); |  | ||||||
| 	grid-gap: 12px; |  | ||||||
| 
 |  | ||||||
| 	> .instance:hover { |  | ||||||
| 		text-decoration: none; |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| </style> |  | ||||||
|  | @ -1,27 +0,0 @@ | ||||||
| <template><MkStickyContainer> |  | ||||||
| 	<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template> |  | ||||||
| 		<MkSpacer :content-max="800"> |  | ||||||
| 	<XNotes :pagination="pagination"/> |  | ||||||
| </MkSpacer></MkStickyContainer> |  | ||||||
| </template> |  | ||||||
| 
 |  | ||||||
| <script lang="ts" setup> |  | ||||||
| import XNotes from '@/components/notes.vue'; |  | ||||||
| import { i18n } from '@/i18n'; |  | ||||||
| import { definePageMetadata } from '@/scripts/page-metadata'; |  | ||||||
| 
 |  | ||||||
| const pagination = { |  | ||||||
| 	endpoint: 'notes/mentions' as const, |  | ||||||
| 	limit: 10, |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| const headerActions = $computed(() => []); |  | ||||||
| 
 |  | ||||||
| const headerTabs = $computed(() => []); |  | ||||||
| 
 |  | ||||||
| definePageMetadata({ |  | ||||||
| 	title: i18n.ts.mentions, |  | ||||||
| 	icon: 'fas fa-at', |  | ||||||
| 	bg: 'var(--bg)', |  | ||||||
| }); |  | ||||||
| </script> |  | ||||||
|  | @ -1,30 +0,0 @@ | ||||||
| <template><MkStickyContainer> |  | ||||||
| 	<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template> |  | ||||||
| 		<MkSpacer :content-max="800"> |  | ||||||
| 	<XNotes :pagination="pagination"/> |  | ||||||
| </MkSpacer></MkStickyContainer> |  | ||||||
| </template> |  | ||||||
| 
 |  | ||||||
| <script lang="ts" setup> |  | ||||||
| import XNotes from '@/components/notes.vue'; |  | ||||||
| import { i18n } from '@/i18n'; |  | ||||||
| import { definePageMetadata } from '@/scripts/page-metadata'; |  | ||||||
| 
 |  | ||||||
| const pagination = { |  | ||||||
| 	endpoint: 'notes/mentions' as const, |  | ||||||
| 	limit: 10, |  | ||||||
| 	params: { |  | ||||||
| 		visibility: 'specified', |  | ||||||
| 	}, |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| const headerActions = $computed(() => []); |  | ||||||
| 
 |  | ||||||
| const headerTabs = $computed(() => []); |  | ||||||
| 
 |  | ||||||
| definePageMetadata({ |  | ||||||
| 	title: i18n.ts.directNotes, |  | ||||||
| 	icon: 'fas fa-envelope', |  | ||||||
| 	bg: 'var(--bg)', |  | ||||||
| }); |  | ||||||
| </script> |  | ||||||
|  | @ -2,8 +2,14 @@ | ||||||
| <MkStickyContainer> | <MkStickyContainer> | ||||||
| 	<template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template> | 	<template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template> | ||||||
| 	<MkSpacer :content-max="800"> | 	<MkSpacer :content-max="800"> | ||||||
| 		<div class="clupoqwt"> | 		<div v-if="tab === 'all' || tab === 'unread'"> | ||||||
| 			<XNotifications class="notifications" :include-types="includeTypes" :unread-only="tab === 'unread'"/> | 			<XNotifications class="notifications" :include-types="includeTypes" :unread-only="unreadOnly"/> | ||||||
|  | 		</div> | ||||||
|  | 		<div v-else-if="tab === 'mentions'"> | ||||||
|  | 			<XNotes :pagination="mentionsPagination"/> | ||||||
|  | 		</div> | ||||||
|  | 		<div v-else-if="tab === 'directNotes'"> | ||||||
|  | 			<XNotes :pagination="directNotesPagination"/> | ||||||
| 		</div> | 		</div> | ||||||
| 	</MkSpacer> | 	</MkSpacer> | ||||||
| </MkStickyContainer> | </MkStickyContainer> | ||||||
|  | @ -13,12 +19,27 @@ | ||||||
| import { computed } from 'vue'; | import { computed } from 'vue'; | ||||||
| import { notificationTypes } from 'misskey-js'; | import { notificationTypes } from 'misskey-js'; | ||||||
| import XNotifications from '@/components/notifications.vue'; | import XNotifications from '@/components/notifications.vue'; | ||||||
|  | import XNotes from '@/components/notes.vue'; | ||||||
| import * as os from '@/os'; | import * as os from '@/os'; | ||||||
| import { i18n } from '@/i18n'; | import { i18n } from '@/i18n'; | ||||||
| import { definePageMetadata } from '@/scripts/page-metadata'; | import { definePageMetadata } from '@/scripts/page-metadata'; | ||||||
| 
 | 
 | ||||||
| let tab = $ref('all'); | let tab = $ref('all'); | ||||||
| let includeTypes = $ref<string[] | null>(null); | let includeTypes = $ref<string[] | null>(null); | ||||||
|  | let unreadOnly = $computed(() => tab === 'unread'); | ||||||
|  | 
 | ||||||
|  | const mentionsPagination = { | ||||||
|  | 	endpoint: 'notes/mentions' as const, | ||||||
|  | 	limit: 10, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const directNotesPagination = { | ||||||
|  | 	endpoint: 'notes/mentions' as const, | ||||||
|  | 	limit: 10, | ||||||
|  | 	params: { | ||||||
|  | 		visibility: 'specified', | ||||||
|  | 	}, | ||||||
|  | }; | ||||||
| 
 | 
 | ||||||
| function setFilter(ev) { | function setFilter(ev) { | ||||||
| 	const typeItems = notificationTypes.map(t => ({ | 	const typeItems = notificationTypes.map(t => ({ | ||||||
|  | @ -38,18 +59,18 @@ function setFilter(ev) { | ||||||
| 	os.popupMenu(items, ev.currentTarget ?? ev.target); | 	os.popupMenu(items, ev.currentTarget ?? ev.target); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| const headerActions = $computed(() => [{ | const headerActions = $computed(() => [tab === 'all' ? { | ||||||
| 	text: i18n.ts.filter, | 	text: i18n.ts.filter, | ||||||
| 	icon: 'fas fa-filter', | 	icon: 'fas fa-filter', | ||||||
| 	highlighted: includeTypes != null, | 	highlighted: includeTypes != null, | ||||||
| 	handler: setFilter, | 	handler: setFilter, | ||||||
| }, { | } : undefined, tab === 'all' ? { | ||||||
| 	text: i18n.ts.markAllAsRead, | 	text: i18n.ts.markAllAsRead, | ||||||
| 	icon: 'fas fa-check', | 	icon: 'fas fa-check', | ||||||
| 	handler: () => { | 	handler: () => { | ||||||
| 		os.apiWithDialog('notifications/mark-all-as-read'); | 		os.apiWithDialog('notifications/mark-all-as-read'); | ||||||
| 	}, | 	}, | ||||||
| }]); | } : undefined].filter(x => x !== undefined)); | ||||||
| 
 | 
 | ||||||
| const headerTabs = $computed(() => [{ | const headerTabs = $computed(() => [{ | ||||||
| 	key: 'all', | 	key: 'all', | ||||||
|  | @ -57,6 +78,14 @@ const headerTabs = $computed(() => [{ | ||||||
| }, { | }, { | ||||||
| 	key: 'unread', | 	key: 'unread', | ||||||
| 	title: i18n.ts.unread, | 	title: i18n.ts.unread, | ||||||
|  | }, { | ||||||
|  | 	key: 'mentions', | ||||||
|  | 	title: i18n.ts.mentions, | ||||||
|  | 	icon: 'fas fa-at', | ||||||
|  | }, { | ||||||
|  | 	key: 'directNotes', | ||||||
|  | 	title: i18n.ts.directNotes, | ||||||
|  | 	icon: 'fas fa-envelope', | ||||||
| }]); | }]); | ||||||
| 
 | 
 | ||||||
| definePageMetadata(computed(() => ({ | definePageMetadata(computed(() => ({ | ||||||
|  | @ -65,8 +94,3 @@ definePageMetadata(computed(() => ({ | ||||||
| 	bg: 'var(--bg)', | 	bg: 'var(--bg)', | ||||||
| }))); | }))); | ||||||
| </script> | </script> | ||||||
| 
 |  | ||||||
| <style lang="scss" scoped> |  | ||||||
| .clupoqwt { |  | ||||||
| } |  | ||||||
| </style> |  | ||||||
|  |  | ||||||
|  | @ -1,6 +1,6 @@ | ||||||
| <template> | <template> | ||||||
| <MkContainer> | <MkContainer> | ||||||
| 	<template #header><i class="fas fa-chart-bar" style="margin-right: 0.5em;"></i>{{ $ts.activity }}</template> | 	<template #header><i class="fas fa-chart-simple" style="margin-right: 0.5em;"></i>{{ $ts.activity }}</template> | ||||||
| 	<template #func> | 	<template #func> | ||||||
| 		<button class="_button" @click="showMenu"> | 		<button class="_button" @click="showMenu"> | ||||||
| 			<i class="fas fa-ellipsis-h"></i> | 			<i class="fas fa-ellipsis-h"></i> | ||||||
|  | @ -36,8 +36,8 @@ function showMenu(ev: MouseEvent) { | ||||||
| 		active: true, | 		active: true, | ||||||
| 		action: () => { | 		action: () => { | ||||||
| 			chartSrc = 'per-user-notes'; | 			chartSrc = 'per-user-notes'; | ||||||
| 		} | 		}, | ||||||
| 	}/*, { | 	},/*, { | ||||||
| 		text: i18n.ts.following, | 		text: i18n.ts.following, | ||||||
| 		action: () => { | 		action: () => { | ||||||
| 			chartSrc = 'per-user-following'; | 			chartSrc = 'per-user-following'; | ||||||
|  |  | ||||||
|  | @ -65,12 +65,6 @@ export const routes = [{ | ||||||
| }, { | }, { | ||||||
| 	path: '/explore', | 	path: '/explore', | ||||||
| 	component: page(() => import('./pages/explore.vue')), | 	component: page(() => import('./pages/explore.vue')), | ||||||
| }, { |  | ||||||
| 	path: '/federation', |  | ||||||
| 	component: page(() => import('./pages/federation.vue')), |  | ||||||
| }, { |  | ||||||
| 	path: '/emojis', |  | ||||||
| 	component: page(() => import('./pages/emojis.vue')), |  | ||||||
| }, { | }, { | ||||||
| 	path: '/search', | 	path: '/search', | ||||||
| 	component: page(() => import('./pages/search.vue')), | 	component: page(() => import('./pages/search.vue')), | ||||||
|  | @ -156,12 +150,6 @@ export const routes = [{ | ||||||
| }, { | }, { | ||||||
| 	path: '/my/favorites', | 	path: '/my/favorites', | ||||||
| 	component: page(() => import('./pages/favorites.vue')), | 	component: page(() => import('./pages/favorites.vue')), | ||||||
| }, { |  | ||||||
| 	path: '/my/messages', |  | ||||||
| 	component: page(() => import('./pages/messages.vue')), |  | ||||||
| }, { |  | ||||||
| 	path: '/my/mentions', |  | ||||||
| 	component: page(() => import('./pages/mentions.vue')), |  | ||||||
| }, { | }, { | ||||||
| 	name: 'messaging', | 	name: 'messaging', | ||||||
| 	path: '/my/messaging', | 	path: '/my/messaging', | ||||||
|  |  | ||||||
|  | @ -1,6 +1,6 @@ | ||||||
| <template> | <template> | ||||||
| <MkContainer :show-header="widgetProps.showHeader" :naked="widgetProps.transparent" class="mkw-activity"> | <MkContainer :show-header="widgetProps.showHeader" :naked="widgetProps.transparent" class="mkw-activity"> | ||||||
| 	<template #header><i class="fas fa-chart-bar"></i>{{ $ts._widgets.activity }}</template> | 	<template #header><i class="fas fa-chart-simple"></i>{{ $ts._widgets.activity }}</template> | ||||||
| 	<template #func><button class="_button" @click="toggleView()"><i class="fas fa-sort"></i></button></template> | 	<template #func><button class="_button" @click="toggleView()"><i class="fas fa-sort"></i></button></template> | ||||||
| 
 | 
 | ||||||
| 	<div> | 	<div> | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue