tweak ui
This commit is contained in:
		
							parent
							
								
									23efba6193
								
							
						
					
					
						commit
						6a5713f5e5
					
				
					 14 changed files with 231 additions and 624 deletions
				
			
		|  | @ -170,10 +170,10 @@ export default defineComponent({ | |||
| 					aspectRatio: props.aspectRatio || 2.5, | ||||
| 					layout: { | ||||
| 						padding: { | ||||
| 							left: 16, | ||||
| 							right: 16, | ||||
| 							top: 16, | ||||
| 							bottom: 8, | ||||
| 							left: 0, | ||||
| 							right: 0, | ||||
| 							top: 0, | ||||
| 							bottom: 0, | ||||
| 						}, | ||||
| 					}, | ||||
| 					scales: { | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| <template> | ||||
| <div v-size="{ max: [500] }" v-sticky-container class="vrtktovh _formBlock"> | ||||
| <div class="vrtktovh _formBlock"> | ||||
| 	<div class="label"><slot name="label"></slot></div> | ||||
| 	<div class="main _formRoot"> | ||||
| 		<slot></slot> | ||||
|  | @ -12,10 +12,8 @@ | |||
| 
 | ||||
| <style lang="scss" scoped> | ||||
| .vrtktovh { | ||||
| 	margin: 0; | ||||
| 	border-top: solid 0.5px var(--divider); | ||||
| 	border-bottom: solid 0.5px var(--divider); | ||||
| 	padding: 24px 0; | ||||
| 
 | ||||
| 	& + .vrtktovh { | ||||
| 		border-top: none; | ||||
|  | @ -31,7 +29,7 @@ | |||
| 
 | ||||
| 	> .label { | ||||
| 		font-weight: bold; | ||||
| 		padding: 0 0 16px 0; | ||||
| 		margin: 1.5em 0 16px 0; | ||||
| 
 | ||||
| 		&:empty { | ||||
| 			display: none; | ||||
|  | @ -39,6 +37,7 @@ | |||
| 	} | ||||
| 
 | ||||
| 	> .main { | ||||
| 		margin: 1.5em 0; | ||||
| 	} | ||||
| } | ||||
| </style> | ||||
|  |  | |||
|  | @ -111,7 +111,7 @@ export default defineComponent({ | |||
| 	} | ||||
| 
 | ||||
| 	> .label { | ||||
| 		margin-left: 16px; | ||||
| 		margin-left: 12px; | ||||
| 		margin-top: 2px; | ||||
| 		display: block; | ||||
| 		transition: inherit; | ||||
|  |  | |||
|  | @ -40,7 +40,7 @@ export default defineComponent({ | |||
| 				return; | ||||
| 			} | ||||
| 
 | ||||
| 			if (rect.width > props.contentMax || rect.width > 500) { | ||||
| 			if (rect.width > props.contentMax || (rect.width > 360 && window.innerWidth > 400)) { | ||||
| 				margin.value = props.marginMax; | ||||
| 			} else { | ||||
| 				margin.value = props.marginMin; | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| <template> | ||||
| <div class="zbcjwnqg" style="margin-top: -8px;"> | ||||
| <div class="zbcjwnqg"> | ||||
| 	<div class="selects" style="display: flex;"> | ||||
| 		<MkSelect v-model="chartSrc" style="margin: 0; flex: 1;"> | ||||
| 			<optgroup :label="$ts.federation"> | ||||
|  | @ -29,16 +29,16 @@ | |||
| 			<option value="day">{{ $ts.perDay }}</option> | ||||
| 		</MkSelect> | ||||
| 	</div> | ||||
| 	<MkChart :src="chartSrc" :span="chartSpan" :limit="chartLimit" :detailed="detailed"></MkChart> | ||||
| 	<div class="chart"> | ||||
| 		<MkChart :src="chartSrc" :span="chartSpan" :limit="chartLimit" :detailed="detailed"></MkChart> | ||||
| 	</div> | ||||
| </div> | ||||
| </template> | ||||
| 
 | ||||
| <script lang="ts"> | ||||
| import { defineComponent, onMounted, ref, watch } from 'vue'; | ||||
| import { defineComponent, ref } from 'vue'; | ||||
| import MkSelect from '@/components/form/select.vue'; | ||||
| import MkChart from '@/components/chart.vue'; | ||||
| import * as os from '@/os'; | ||||
| import { defaultStore } from '@/store'; | ||||
| 
 | ||||
| export default defineComponent({ | ||||
| 	components: { | ||||
|  | @ -74,7 +74,10 @@ export default defineComponent({ | |||
| <style lang="scss" scoped> | ||||
| .zbcjwnqg { | ||||
| 	> .selects { | ||||
| 		padding: 8px 16px 0 16px; | ||||
| 	} | ||||
| 
 | ||||
| 	> .chart { | ||||
| 		padding: 8px 0 0 0; | ||||
| 	} | ||||
| } | ||||
| </style> | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| <template> | ||||
| <div class="alqyeyti"> | ||||
| <div class="alqyeyti" :class="{ oneline }"> | ||||
| 	<div class="key"> | ||||
| 		<slot name="key"></slot> | ||||
| 	</div> | ||||
|  | @ -22,6 +22,11 @@ export default defineComponent({ | |||
| 			required: false, | ||||
| 			default: null, | ||||
| 		}, | ||||
| 		oneline: { | ||||
| 			type: Boolean, | ||||
| 			required: false, | ||||
| 			default: false, | ||||
| 		}, | ||||
| 	}, | ||||
| 
 | ||||
| 	setup(props) { | ||||
|  | @ -39,10 +44,30 @@ export default defineComponent({ | |||
| 
 | ||||
| <style lang="scss" scoped> | ||||
| .alqyeyti { | ||||
| 	> .key, > .value { | ||||
| 		white-space: nowrap; | ||||
| 		overflow: hidden; | ||||
| 		text-overflow: ellipsis; | ||||
| 	} | ||||
| 
 | ||||
| 	> .key { | ||||
| 		font-size: 0.85em; | ||||
| 		padding: 0 0 0.25em 0; | ||||
| 		opacity: 0.75; | ||||
| 	} | ||||
| 
 | ||||
| 	&.oneline { | ||||
| 		display: flex; | ||||
| 
 | ||||
| 		> .key { | ||||
| 			width: 30%; | ||||
| 			font-size: 1em; | ||||
| 			padding: 0 8px 0 0; | ||||
| 		} | ||||
| 
 | ||||
| 		> .value { | ||||
| 			width: 70%; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| </style> | ||||
|  |  | |||
|  | @ -93,7 +93,8 @@ export default defineComponent({ | |||
| 		return { | ||||
| 			[symbols.PAGE_INFO]: { | ||||
| 				title: this.$ts.instanceInfo, | ||||
| 				icon: 'fas fa-info-circle' | ||||
| 				icon: 'fas fa-info-circle', | ||||
| 				bg: 'var(--bg)', | ||||
| 			}, | ||||
| 			host, | ||||
| 			version, | ||||
|  |  | |||
|  | @ -1,291 +0,0 @@ | |||
| <template> | ||||
| <XModalWindow ref="dialog" | ||||
| 	:width="520" | ||||
| 	:height="500" | ||||
| 	@close="$refs.dialog.close()" | ||||
| 	@closed="$emit('closed')" | ||||
| > | ||||
| 	<template #header>{{ instance.host }}</template> | ||||
| 	<div class="mk-instance-info"> | ||||
| 		<div class="_table section"> | ||||
| 			<div class="_row"> | ||||
| 				<div class="_cell"> | ||||
| 					<div class="_label">{{ $ts.software }}</div> | ||||
| 					<div class="_data">{{ instance.softwareName || '?' }}</div> | ||||
| 				</div> | ||||
| 				<div class="_cell"> | ||||
| 					<div class="_label">{{ $ts.version }}</div> | ||||
| 					<div class="_data">{{ instance.softwareVersion || '?' }}</div> | ||||
| 				</div> | ||||
| 			</div> | ||||
| 		</div> | ||||
| 		<div class="_table data section"> | ||||
| 			<div class="_row"> | ||||
| 				<div class="_cell"> | ||||
| 					<div class="_label">{{ $ts.registeredAt }}</div> | ||||
| 					<div class="_data">{{ new Date(instance.caughtAt).toLocaleString() }} (<MkTime :time="instance.caughtAt"/>)</div> | ||||
| 				</div> | ||||
| 			</div> | ||||
| 			<div class="_row"> | ||||
| 				<div class="_cell"> | ||||
| 					<div class="_label">{{ $ts.following }}</div> | ||||
| 					<button class="_data _textButton" @click="showFollowing()">{{ number(instance.followingCount) }}</button> | ||||
| 				</div> | ||||
| 				<div class="_cell"> | ||||
| 					<div class="_label">{{ $ts.followers }}</div> | ||||
| 					<button class="_data _textButton" @click="showFollowers()">{{ number(instance.followersCount) }}</button> | ||||
| 				</div> | ||||
| 			</div> | ||||
| 			<div class="_row"> | ||||
| 				<div class="_cell"> | ||||
| 					<div class="_label">{{ $ts.users }}</div> | ||||
| 					<button class="_data _textButton" @click="showUsers()">{{ number(instance.usersCount) }}</button> | ||||
| 				</div> | ||||
| 				<div class="_cell"> | ||||
| 					<div class="_label">{{ $ts.notes }}</div> | ||||
| 					<div class="_data">{{ number(instance.notesCount) }}</div> | ||||
| 				</div> | ||||
| 			</div> | ||||
| 			<div class="_row"> | ||||
| 				<div class="_cell"> | ||||
| 					<div class="_label">{{ $ts.files }}</div> | ||||
| 					<div class="_data">{{ number(instance.driveFiles) }}</div> | ||||
| 				</div> | ||||
| 				<div class="_cell"> | ||||
| 					<div class="_label">{{ $ts.storageUsage }}</div> | ||||
| 					<div class="_data">{{ bytes(instance.driveUsage) }}</div> | ||||
| 				</div> | ||||
| 			</div> | ||||
| 			<div class="_row"> | ||||
| 				<div class="_cell"> | ||||
| 					<div class="_label">{{ $ts.latestRequestSentAt }}</div> | ||||
| 					<div class="_data"><MkTime v-if="instance.latestRequestSentAt" :time="instance.latestRequestSentAt"/><span v-else>N/A</span></div> | ||||
| 				</div> | ||||
| 				<div class="_cell"> | ||||
| 					<div class="_label">{{ $ts.latestStatus }}</div> | ||||
| 					<div class="_data">{{ instance.latestStatus ? instance.latestStatus : 'N/A' }}</div> | ||||
| 				</div> | ||||
| 			</div> | ||||
| 			<div class="_row"> | ||||
| 				<div class="_cell"> | ||||
| 					<div class="_label">{{ $ts.latestRequestReceivedAt }}</div> | ||||
| 					<div class="_data"><MkTime v-if="instance.latestRequestReceivedAt" :time="instance.latestRequestReceivedAt"/><span v-else>N/A</span></div> | ||||
| 				</div> | ||||
| 			</div> | ||||
| 		</div> | ||||
| 		<div class="chart"> | ||||
| 			<div class="header"> | ||||
| 				<span class="label">{{ $ts.charts }}</span> | ||||
| 				<div class="selects"> | ||||
| 					<MkSelect v-model="chartSrc" style="margin: 0; flex: 1;"> | ||||
| 						<option value="instance-requests">{{ $ts._instanceCharts.requests }}</option> | ||||
| 						<option value="instance-users">{{ $ts._instanceCharts.users }}</option> | ||||
| 						<option value="instance-users-total">{{ $ts._instanceCharts.usersTotal }}</option> | ||||
| 						<option value="instance-notes">{{ $ts._instanceCharts.notes }}</option> | ||||
| 						<option value="instance-notes-total">{{ $ts._instanceCharts.notesTotal }}</option> | ||||
| 						<option value="instance-ff">{{ $ts._instanceCharts.ff }}</option> | ||||
| 						<option value="instance-ff-total">{{ $ts._instanceCharts.ffTotal }}</option> | ||||
| 						<option value="instance-drive-usage">{{ $ts._instanceCharts.cacheSize }}</option> | ||||
| 						<option value="instance-drive-usage-total">{{ $ts._instanceCharts.cacheSizeTotal }}</option> | ||||
| 						<option value="instance-drive-files">{{ $ts._instanceCharts.files }}</option> | ||||
| 						<option value="instance-drive-files-total">{{ $ts._instanceCharts.filesTotal }}</option> | ||||
| 					</MkSelect> | ||||
| 					<MkSelect v-model="chartSpan" style="margin: 0;"> | ||||
| 						<option value="hour">{{ $ts.perHour }}</option> | ||||
| 						<option value="day">{{ $ts.perDay }}</option> | ||||
| 					</MkSelect> | ||||
| 				</div> | ||||
| 			</div> | ||||
| 			<div class="chart"> | ||||
| 				<MkChart :src="chartSrc" :span="chartSpan" :limit="90" :detailed="true"></MkChart> | ||||
| 			</div> | ||||
| 		</div> | ||||
| 		<div class="operations section"> | ||||
| 			<span class="label">{{ $ts.operations }}</span> | ||||
| 			<MkSwitch v-model="isSuspended" class="switch">{{ $ts.stopActivityDelivery }}</MkSwitch> | ||||
| 			<MkSwitch :model-value="isBlocked" class="switch" @update:modelValue="changeBlock">{{ $ts.blockThisInstance }}</MkSwitch> | ||||
| 			<details> | ||||
| 				<summary>{{ $ts.deleteAllFiles }}</summary> | ||||
| 				<MkButton style="margin: 0.5em 0 0.5em 0;" @click="deleteAllFiles()"><i class="fas fa-trash-alt"></i> {{ $ts.deleteAllFiles }}</MkButton> | ||||
| 			</details> | ||||
| 			<details> | ||||
| 				<summary>{{ $ts.removeAllFollowing }}</summary> | ||||
| 				<MkButton style="margin: 0.5em 0 0.5em 0;" @click="removeAllFollowing()"><i class="fas fa-minus-circle"></i> {{ $ts.removeAllFollowing }}</MkButton> | ||||
| 				<MkInfo warn>{{ $t('removeAllFollowingDescription', { host: instance.host }) }}</MkInfo> | ||||
| 			</details> | ||||
| 		</div> | ||||
| 		<details class="metadata section"> | ||||
| 			<summary class="label">{{ $ts.metadata }}</summary> | ||||
| 			<pre><code>{{ JSON.stringify(instance, null, 2) }}</code></pre> | ||||
| 		</details> | ||||
| 	</div> | ||||
| </XModalWindow> | ||||
| </template> | ||||
| 
 | ||||
| <script lang="ts"> | ||||
| import { defineComponent, markRaw } from 'vue'; | ||||
| import XModalWindow from '@/components/ui/modal-window.vue'; | ||||
| import MkSelect from '@/components/form/select.vue'; | ||||
| import MkButton from '@/components/ui/button.vue'; | ||||
| import MkSwitch from '@/components/form/switch.vue'; | ||||
| import MkInfo from '@/components/ui/info.vue'; | ||||
| import MkChart from '@/components/chart.vue'; | ||||
| import bytes from '@/filters/bytes'; | ||||
| import number from '@/filters/number'; | ||||
| import * as os from '@/os'; | ||||
| 
 | ||||
| export default defineComponent({ | ||||
| 	components: { | ||||
| 		XModalWindow, | ||||
| 		MkSelect, | ||||
| 		MkButton, | ||||
| 		MkSwitch, | ||||
| 		MkInfo, | ||||
| 		MkChart, | ||||
| 	}, | ||||
| 
 | ||||
| 	props: { | ||||
| 		instance: { | ||||
| 			type: Object, | ||||
| 			required: true | ||||
| 		} | ||||
| 	}, | ||||
| 
 | ||||
| 	emits: ['closed'], | ||||
| 
 | ||||
| 	data() { | ||||
| 		return { | ||||
| 			isSuspended: this.instance.isSuspended, | ||||
| 			chartSrc: 'requests', | ||||
| 			chartSpan: 'hour', | ||||
| 		}; | ||||
| 	}, | ||||
| 
 | ||||
| 	computed: { | ||||
| 		meta() { | ||||
| 			return this.$instance; | ||||
| 		}, | ||||
| 
 | ||||
| 		isBlocked() { | ||||
| 			return this.meta && this.meta.blockedHosts && this.meta.blockedHosts.includes(this.instance.host); | ||||
| 		} | ||||
| 	}, | ||||
| 
 | ||||
| 	watch: { | ||||
| 		isSuspended() { | ||||
| 			os.api('admin/federation/update-instance', { | ||||
| 				host: this.instance.host, | ||||
| 				isSuspended: this.isSuspended | ||||
| 			}); | ||||
| 		}, | ||||
| 	}, | ||||
| 
 | ||||
| 	methods: { | ||||
| 		changeBlock(e) { | ||||
| 			os.api('admin/update-meta', { | ||||
| 				blockedHosts: this.isBlocked ? this.meta.blockedHosts.concat([this.instance.host]) : this.meta.blockedHosts.filter(x => x !== this.instance.host) | ||||
| 			}); | ||||
| 		}, | ||||
| 
 | ||||
| 		removeAllFollowing() { | ||||
| 			os.apiWithDialog('admin/federation/remove-all-following', { | ||||
| 				host: this.instance.host | ||||
| 			}); | ||||
| 		}, | ||||
| 
 | ||||
| 		deleteAllFiles() { | ||||
| 			os.apiWithDialog('admin/federation/delete-all-files', { | ||||
| 				host: this.instance.host | ||||
| 			}); | ||||
| 		}, | ||||
| 
 | ||||
| 		showFollowing() { | ||||
| 			// TODO: ページ遷移 | ||||
| 		}, | ||||
| 
 | ||||
| 		showFollowers() { | ||||
| 			// TODO: ページ遷移 | ||||
| 		}, | ||||
| 
 | ||||
| 		showUsers() { | ||||
| 			// TODO: ページ遷移 | ||||
| 		}, | ||||
| 
 | ||||
| 		bytes, | ||||
| 
 | ||||
| 		number | ||||
| 	} | ||||
| }); | ||||
| </script> | ||||
| 
 | ||||
| <style lang="scss" scoped> | ||||
| .mk-instance-info { | ||||
| 	overflow: auto; | ||||
| 
 | ||||
| 	> .section { | ||||
| 		padding: 16px 32px; | ||||
| 
 | ||||
| 		@media (max-width: 500px) { | ||||
| 			padding: 8px 16px; | ||||
| 		} | ||||
| 
 | ||||
| 		&:not(:first-child) { | ||||
| 			border-top: solid 0.5px var(--divider); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	> .chart { | ||||
| 		border-top: solid 0.5px var(--divider); | ||||
| 		padding: 16px 0 12px 0; | ||||
| 
 | ||||
| 		> .header { | ||||
| 			padding: 0 32px; | ||||
| 
 | ||||
| 			@media (max-width: 500px) { | ||||
| 				padding: 0 16px; | ||||
| 			} | ||||
| 
 | ||||
| 			> .label { | ||||
| 				font-size: 80%; | ||||
| 				opacity: 0.7; | ||||
| 			} | ||||
| 
 | ||||
| 			> .selects { | ||||
| 				display: flex; | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		> .chart { | ||||
| 			padding: 0 16px; | ||||
| 
 | ||||
| 			@media (max-width: 500px) { | ||||
| 				padding: 0; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	> .operations { | ||||
| 		> .label { | ||||
| 			font-size: 80%; | ||||
| 			opacity: 0.7; | ||||
| 		} | ||||
| 
 | ||||
| 		> .switch { | ||||
| 			margin: 16px 0; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	> .metadata { | ||||
| 		> .label { | ||||
| 			font-size: 80%; | ||||
| 			opacity: 0.7; | ||||
| 		} | ||||
| 
 | ||||
| 		> pre > code { | ||||
| 			display: block; | ||||
| 			max-height: 200px; | ||||
| 			overflow: auto; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| </style> | ||||
|  | @ -76,7 +76,6 @@ import MkwFederation from '../../widgets/federation.vue'; | |||
| import { version, url } from '@/config'; | ||||
| import bytes from '@/filters/bytes'; | ||||
| import number from '@/filters/number'; | ||||
| import MkInstanceInfo from './instance.vue'; | ||||
| 
 | ||||
| Chart.register( | ||||
|   ArcElement, | ||||
|  |  | |||
|  | @ -19,7 +19,7 @@ | |||
| 
 | ||||
| 	<MkContainer :foldable="true" class="charts"> | ||||
| 		<template #header><i class="fas fa-chart-bar"></i>{{ $ts.charts }}</template> | ||||
| 		<div style="padding-top: 12px;"> | ||||
| 		<div style="padding: 12px;"> | ||||
| 			<MkInstanceStats :chart-limit="500" :detailed="true"/> | ||||
| 		</div> | ||||
| 	</MkContainer> | ||||
|  | @ -77,7 +77,6 @@ import MkQueueChart from '@/components/queue-chart.vue'; | |||
| import { version, url } from '@/config'; | ||||
| import bytes from '@/filters/bytes'; | ||||
| import number from '@/filters/number'; | ||||
| import MkInstanceInfo from './instance.vue'; | ||||
| import XMetrics from './metrics.vue'; | ||||
| import * as os from '@/os'; | ||||
| import { stream } from '@/stream'; | ||||
|  | @ -159,9 +158,7 @@ export default defineComponent({ | |||
| 					host: q | ||||
| 				}); | ||||
| 			} | ||||
| 			os.popup(MkInstanceInfo, { | ||||
| 				instance: instance | ||||
| 			}, {}, 'closed'); | ||||
| 			// TODO | ||||
| 		}, | ||||
| 
 | ||||
| 		bytes, | ||||
|  |  | |||
|  | @ -1,70 +1,71 @@ | |||
| <template> | ||||
| <FormBase> | ||||
| 	<FormGroup v-if="instance"> | ||||
| 		<template #label>{{ instance.host }}</template> | ||||
| 		<FormGroup> | ||||
| 			<div class="_debobigegoItem"> | ||||
| 				<div class="_debobigegoPanel fnfelxur"> | ||||
| 					<img :src="instance.iconUrl || instance.faviconUrl" alt="" class="icon"/> | ||||
| 				</div> | ||||
| 			</div> | ||||
| 			<FormKeyValueView> | ||||
| 				<template #key>Name</template> | ||||
| 				<template #value><span class="_monospace">{{ instance.name || `(${$ts.unknown})` }}</span></template> | ||||
| 			</FormKeyValueView> | ||||
| 		</FormGroup> | ||||
| <MkSpacer :content-max="600" :margin-min="16" :margin-max="32"> | ||||
| 	<div v-if="instance" class="_formRoot"> | ||||
| 		<div class="fnfelxur"> | ||||
| 			<img :src="instance.iconUrl || instance.faviconUrl" alt="" class="icon"/> | ||||
| 		</div> | ||||
| 		<MkKeyValue :copy="host" oneline style="margin: 1em 0;"> | ||||
| 			<template #key>Host</template> | ||||
| 			<template #value><span class="_monospace"><MkLink :url="`https://${host}`">{{ host }}</MkLink></span></template> | ||||
| 		</MkKeyValue> | ||||
| 		<MkKeyValue oneline style="margin: 1em 0;"> | ||||
| 			<template #key>Name</template> | ||||
| 			<template #value>{{ instance.name || `(${$ts.unknown})` }}</template> | ||||
| 		</MkKeyValue> | ||||
| 		<MkKeyValue> | ||||
| 			<template #key>{{ $ts.description }}</template> | ||||
| 			<template #value>{{ instance.description }}</template> | ||||
| 		</MkKeyValue> | ||||
| 		<MkKeyValue oneline style="margin: 1em 0;"> | ||||
| 			<template #key>{{ $ts.software }}</template> | ||||
| 			<template #value><span class="_monospace">{{ instance.softwareName || `(${$ts.unknown})` }} / {{ instance.softwareVersion || `(${$ts.unknown})` }}</span></template> | ||||
| 		</MkKeyValue> | ||||
| 		<MkKeyValue oneline style="margin: 1em 0;"> | ||||
| 			<template #key>{{ $ts.administrator }}</template> | ||||
| 			<template #value>{{ instance.maintainerName || `(${$ts.unknown})` }} ({{ instance.maintainerEmail || `(${$ts.unknown})` }})</template> | ||||
| 		</MkKeyValue> | ||||
| 
 | ||||
| 		<FormButton v-if="$i.isAdmin || $i.isModerator" primary @click="info">{{ $ts.settings }}</FormButton> | ||||
| 		<FormSection v-if="iAmModerator"> | ||||
| 			<template #label>Moderation</template> | ||||
| 			<FormSwitch v-model="suspended" class="_formBlock" @update:modelValue="toggleSuspend">{{ $ts.stopActivityDelivery }}</FormSwitch> | ||||
| 			<FormSwitch :model-value="isBlocked" class="switch" @update:modelValue="changeBlock">{{ $ts.blockThisInstance }}</FormSwitch> | ||||
| 		</FormSection> | ||||
| 
 | ||||
| 		<FormTextarea readonly :value="instance.description"> | ||||
| 			<span>{{ $ts.description }}</span> | ||||
| 		</FormTextarea> | ||||
| 
 | ||||
| 		<FormGroup> | ||||
| 			<FormKeyValueView> | ||||
| 				<template #key>{{ $ts.software }}</template> | ||||
| 				<template #value><span class="_monospace">{{ instance.softwareName || `(${$ts.unknown})` }}</span></template> | ||||
| 			</FormKeyValueView> | ||||
| 			<FormKeyValueView> | ||||
| 				<template #key>{{ $ts.version }}</template> | ||||
| 				<template #value><span class="_monospace">{{ instance.softwareVersion || `(${$ts.unknown})` }}</span></template> | ||||
| 			</FormKeyValueView> | ||||
| 		</FormGroup> | ||||
| 		<FormGroup> | ||||
| 			<FormKeyValueView> | ||||
| 				<template #key>{{ $ts.administrator }}</template> | ||||
| 				<template #value><span class="_monospace">{{ instance.maintainerName || `(${$ts.unknown})` }}</span></template> | ||||
| 			</FormKeyValueView> | ||||
| 			<FormKeyValueView> | ||||
| 				<template #key>{{ $ts.contact }}</template> | ||||
| 				<template #value><span class="_monospace">{{ instance.maintainerEmail || `(${$ts.unknown})` }}</span></template> | ||||
| 			</FormKeyValueView> | ||||
| 		</FormGroup> | ||||
| 		<FormGroup> | ||||
| 			<FormKeyValueView> | ||||
| 		<FormSection> | ||||
| 			<MkKeyValue oneline style="margin: 1em 0;"> | ||||
| 				<template #key>{{ $ts.registeredAt }}</template> | ||||
| 				<template #value><MkTime mode="detail" :time="instance.caughtAt"/></template> | ||||
| 			</MkKeyValue> | ||||
| 			<MkKeyValue oneline style="margin: 1em 0;"> | ||||
| 				<template #key>{{ $ts.updatedAt }}</template> | ||||
| 				<template #value><MkTime mode="detail" :time="instance.infoUpdatedAt"/></template> | ||||
| 			</MkKeyValue> | ||||
| 			<MkKeyValue oneline style="margin: 1em 0;"> | ||||
| 				<template #key>{{ $ts.latestRequestSentAt }}</template> | ||||
| 				<template #value><MkTime v-if="instance.latestRequestSentAt" :time="instance.latestRequestSentAt"/><span v-else>N/A</span></template> | ||||
| 			</FormKeyValueView> | ||||
| 			<FormKeyValueView> | ||||
| 			</MkKeyValue> | ||||
| 			<MkKeyValue oneline style="margin: 1em 0;"> | ||||
| 				<template #key>{{ $ts.latestStatus }}</template> | ||||
| 				<template #value>{{ instance.latestStatus ? instance.latestStatus : 'N/A' }}</template> | ||||
| 			</FormKeyValueView> | ||||
| 			<FormKeyValueView> | ||||
| 			</MkKeyValue> | ||||
| 			<MkKeyValue oneline style="margin: 1em 0;"> | ||||
| 				<template #key>{{ $ts.latestRequestReceivedAt }}</template> | ||||
| 				<template #value><MkTime v-if="instance.latestRequestReceivedAt" :time="instance.latestRequestReceivedAt"/><span v-else>N/A</span></template> | ||||
| 			</FormKeyValueView> | ||||
| 		</FormGroup> | ||||
| 		<FormGroup> | ||||
| 			<FormKeyValueView> | ||||
| 			</MkKeyValue> | ||||
| 		</FormSection> | ||||
| 	 | ||||
| 		<FormSection> | ||||
| 			<MkKeyValue oneline style="margin: 1em 0;"> | ||||
| 				<template #key>Open Registrations</template> | ||||
| 				<template #value>{{ instance.openRegistrations ? $ts.yes : $ts.no }}</template> | ||||
| 			</FormKeyValueView> | ||||
| 		</FormGroup> | ||||
| 		<div class="_debobigegoItem"> | ||||
| 			<div class="_debobigegoLabel">{{ $ts.statistics }}</div> | ||||
| 			<div class="_debobigegoPanel cmhjzshl"> | ||||
| 			</MkKeyValue> | ||||
| 		</FormSection> | ||||
| 
 | ||||
| 		<FormSection> | ||||
| 			<template #label>{{ $ts.statistics }}</template> | ||||
| 			<div class="cmhjzshl"> | ||||
| 				<div class="selects"> | ||||
| 					<MkSelect v-model="chartSrc" style="margin: 0; flex: 1;"> | ||||
| 					<MkSelect v-model="chartSrc" style="margin: 0 10px 0 0; flex: 1;"> | ||||
| 						<option value="instance-requests">{{ $ts._instanceCharts.requests }}</option> | ||||
| 						<option value="instance-users">{{ $ts._instanceCharts.users }}</option> | ||||
| 						<option value="instance-users-total">{{ $ts._instanceCharts.usersTotal }}</option> | ||||
|  | @ -83,86 +84,56 @@ | |||
| 					</MkSelect> | ||||
| 				</div> | ||||
| 				<div class="chart"> | ||||
| 					<MkChart :src="chartSrc" :span="chartSpan" :limit="90" :detailed="true"></MkChart> | ||||
| 					<MkChart :src="chartSrc" :span="chartSpan" :limit="90" :args="{ host: host }" :detailed="true"></MkChart> | ||||
| 				</div> | ||||
| 			</div> | ||||
| 		</div> | ||||
| 		<FormGroup> | ||||
| 			<FormKeyValueView> | ||||
| 				<template #key>{{ $ts.registeredAt }}</template> | ||||
| 				<template #value><MkTime mode="detail" :time="instance.caughtAt"/></template> | ||||
| 			</FormKeyValueView> | ||||
| 			<FormKeyValueView> | ||||
| 				<template #key>{{ $ts.updatedAt }}</template> | ||||
| 				<template #value><MkTime mode="detail" :time="instance.infoUpdatedAt"/></template> | ||||
| 			</FormKeyValueView> | ||||
| 		</FormGroup> | ||||
| 		</FormSection> | ||||
| 
 | ||||
| 		<FormObjectView tall :value="instance"> | ||||
| 			<span>Raw</span> | ||||
| 		</FormObjectView> | ||||
| 		<FormGroup> | ||||
| 
 | ||||
| 		<FormSection> | ||||
| 			<template #label>Well-known resources</template> | ||||
| 			<FormLink :to="`https://${host}/.well-known/host-meta`" external>host-meta</FormLink> | ||||
| 			<FormLink :to="`https://${host}/.well-known/host-meta.json`" external>host-meta.json</FormLink> | ||||
| 			<FormLink :to="`https://${host}/.well-known/nodeinfo`" external>nodeinfo</FormLink> | ||||
| 			<FormLink :to="`https://${host}/robots.txt`" external>robots.txt</FormLink> | ||||
| 			<FormLink :to="`https://${host}/manifest.json`" external>manifest.json</FormLink> | ||||
| 		</FormGroup> | ||||
| 		<FormSuspense v-slot="{ result: dns }" :p="dnsPromiseFactory"> | ||||
| 			<FormGroup> | ||||
| 				<template #label>DNS</template> | ||||
| 				<FormKeyValueView v-for="record in dns.a" :key="record"> | ||||
| 					<template #key>A</template> | ||||
| 					<template #value><span class="_monospace">{{ record }}</span></template> | ||||
| 				</FormKeyValueView> | ||||
| 				<FormKeyValueView v-for="record in dns.aaaa" :key="record"> | ||||
| 					<template #key>AAAA</template> | ||||
| 					<template #value><span class="_monospace">{{ record }}</span></template> | ||||
| 				</FormKeyValueView> | ||||
| 				<FormKeyValueView v-for="record in dns.cname" :key="record"> | ||||
| 					<template #key>CNAME</template> | ||||
| 					<template #value><span class="_monospace">{{ record }}</span></template> | ||||
| 				</FormKeyValueView> | ||||
| 				<FormKeyValueView v-for="record in dns.txt"> | ||||
| 					<template #key>TXT</template> | ||||
| 					<template #value><span class="_monospace">{{ record[0] }}</span></template> | ||||
| 				</FormKeyValueView> | ||||
| 			</FormGroup> | ||||
| 		</FormSuspense> | ||||
| 	</FormGroup> | ||||
| </FormBase> | ||||
| 			<FormLink :to="`https://${host}/.well-known/host-meta`" external style="margin-bottom: 8px;">host-meta</FormLink> | ||||
| 			<FormLink :to="`https://${host}/.well-known/host-meta.json`" external style="margin-bottom: 8px;">host-meta.json</FormLink> | ||||
| 			<FormLink :to="`https://${host}/.well-known/nodeinfo`" external style="margin-bottom: 8px;">nodeinfo</FormLink> | ||||
| 			<FormLink :to="`https://${host}/robots.txt`" external style="margin-bottom: 8px;">robots.txt</FormLink> | ||||
| 			<FormLink :to="`https://${host}/manifest.json`" external style="margin-bottom: 8px;">manifest.json</FormLink> | ||||
| 		</FormSection> | ||||
| 	</div> | ||||
| </MkSpacer> | ||||
| </template> | ||||
| 
 | ||||
| <script lang="ts"> | ||||
| import { defineAsyncComponent, defineComponent } from 'vue'; | ||||
| import MkChart from '@/components/chart.vue'; | ||||
| import FormObjectView from '@/components/debobigego/object-view.vue'; | ||||
| import FormTextarea from '@/components/debobigego/textarea.vue'; | ||||
| import FormLink from '@/components/debobigego/link.vue'; | ||||
| import FormBase from '@/components/debobigego/base.vue'; | ||||
| import FormGroup from '@/components/debobigego/group.vue'; | ||||
| import FormButton from '@/components/debobigego/button.vue'; | ||||
| import FormKeyValueView from '@/components/debobigego/key-value-view.vue'; | ||||
| import FormSuspense from '@/components/debobigego/suspense.vue'; | ||||
| import FormTextarea from '@/components/form/textarea.vue'; | ||||
| import FormLink from '@/components/form/link.vue'; | ||||
| import MkLink from '@/components/link.vue'; | ||||
| import FormSection from '@/components/form/section.vue'; | ||||
| import FormButton from '@/components/ui/button.vue'; | ||||
| import MkKeyValue from '@/components/key-value.vue'; | ||||
| import MkSelect from '@/components/form/select.vue'; | ||||
| import FormSwitch from '@/components/form/switch.vue'; | ||||
| import * as os from '@/os'; | ||||
| import number from '@/filters/number'; | ||||
| import bytes from '@/filters/bytes'; | ||||
| import * as symbols from '@/symbols'; | ||||
| import MkInstanceInfo from '@/pages/admin/instance.vue'; | ||||
| 
 | ||||
| export default defineComponent({ | ||||
| 	components: { | ||||
| 		FormBase, | ||||
| 		FormTextarea, | ||||
| 		FormObjectView, | ||||
| 		FormButton, | ||||
| 		FormLink, | ||||
| 		FormGroup, | ||||
| 		FormKeyValueView, | ||||
| 		FormSuspense, | ||||
| 		FormSection, | ||||
| 		FormSwitch, | ||||
| 		MkKeyValue, | ||||
| 		MkSelect, | ||||
| 		MkChart, | ||||
| 		MkLink, | ||||
| 	}, | ||||
| 
 | ||||
| 	props: { | ||||
|  | @ -175,8 +146,9 @@ export default defineComponent({ | |||
| 	data() { | ||||
| 		return { | ||||
| 			[symbols.PAGE_INFO]: { | ||||
| 				title: this.$ts.instanceInfo, | ||||
| 				title: this.host, | ||||
| 				icon: 'fas fa-info-circle', | ||||
| 				bg: 'var(--bg)', | ||||
| 				actions: [{ | ||||
| 					text: `https://${this.host}`, | ||||
| 					icon: 'fas fa-external-link-alt', | ||||
|  | @ -186,14 +158,22 @@ export default defineComponent({ | |||
| 				}], | ||||
| 			}, | ||||
| 			instance: null, | ||||
| 			dnsPromiseFactory: () => os.api('federation/dns', { | ||||
| 				host: this.host | ||||
| 			}), | ||||
| 			suspended: false, | ||||
| 			chartSrc: 'instance-requests', | ||||
| 			chartSpan: 'hour', | ||||
| 		} | ||||
| 	}, | ||||
| 
 | ||||
| 	computed: { | ||||
| 		iAmModerator(): boolean { | ||||
| 			return this.$i && (this.$i.isAdmin || this.$i.isModerator); | ||||
| 		}, | ||||
| 
 | ||||
| 		isBlocked() { | ||||
| 			return this.instance && this.$instance && this.$instance.blockedHosts && this.$instance.blockedHosts.includes(this.instance.host); | ||||
| 		} | ||||
| 	}, | ||||
| 
 | ||||
| 	mounted() { | ||||
| 		this.fetch(); | ||||
| 	}, | ||||
|  | @ -206,24 +186,30 @@ export default defineComponent({ | |||
| 			this.instance = await os.api('federation/show-instance', { | ||||
| 				host: this.host | ||||
| 			}); | ||||
| 			this.suspended = this.instance.isSuspended; | ||||
| 		}, | ||||
| 
 | ||||
| 		info() { | ||||
| 			os.popup(MkInstanceInfo, { | ||||
| 				instance: this.instance | ||||
| 			}, {}, 'closed'); | ||||
| 		} | ||||
| 		changeBlock(e) { | ||||
| 			os.api('admin/update-meta', { | ||||
| 				blockedHosts: this.isBlocked ? this.meta.blockedHosts.concat([this.instance.host]) : this.meta.blockedHosts.filter(x => x !== this.instance.host) | ||||
| 			}); | ||||
| 		}, | ||||
| 
 | ||||
| 		async toggleSuspend(v) { | ||||
| 			await os.api('admin/federation/update-instance', { | ||||
| 				host: this.instance.host, | ||||
| 				isSuspended: this.suspended | ||||
| 			}); | ||||
| 		}, | ||||
| 	} | ||||
| }); | ||||
| </script> | ||||
| 
 | ||||
| <style lang="scss" scoped> | ||||
| .fnfelxur { | ||||
| 	padding: 16px; | ||||
| 
 | ||||
| 	> .icon { | ||||
| 		display: block; | ||||
| 		margin: auto; | ||||
| 		margin: 0; | ||||
| 		height: 64px; | ||||
| 		border-radius: 8px; | ||||
| 	} | ||||
|  | @ -232,7 +218,7 @@ export default defineComponent({ | |||
| .cmhjzshl { | ||||
| 	> .selects { | ||||
| 		display: flex; | ||||
| 		padding: 16px; | ||||
| 		margin: 0 0 16px 0; | ||||
| 	} | ||||
| } | ||||
| </style> | ||||
|  |  | |||
|  | @ -1,124 +0,0 @@ | |||
| <template> | ||||
| <FormBase> | ||||
| 	<FormSuspense v-slot="{ result: ap }" :p="apPromiseFactory"> | ||||
| 		<FormGroup> | ||||
| 			<template #label>ActivityPub</template> | ||||
| 			<FormKeyValueView> | ||||
| 				<template #key>Type</template> | ||||
| 				<template #value><span class="_monospace">{{ ap.type }}</span></template> | ||||
| 			</FormKeyValueView> | ||||
| 			<FormKeyValueView> | ||||
| 				<template #key>URI</template> | ||||
| 				<template #value><span class="_monospace">{{ ap.id }}</span></template> | ||||
| 			</FormKeyValueView> | ||||
| 			<FormKeyValueView> | ||||
| 				<template #key>URL</template> | ||||
| 				<template #value><span class="_monospace">{{ ap.url }}</span></template> | ||||
| 			</FormKeyValueView> | ||||
| 			<FormGroup> | ||||
| 				<FormKeyValueView> | ||||
| 					<template #key>Inbox</template> | ||||
| 					<template #value><span class="_monospace">{{ ap.inbox }}</span></template> | ||||
| 				</FormKeyValueView> | ||||
| 				<FormKeyValueView> | ||||
| 					<template #key>Shared Inbox</template> | ||||
| 					<template #value><span class="_monospace">{{ ap.sharedInbox || ap.endpoints.sharedInbox }}</span></template> | ||||
| 				</FormKeyValueView> | ||||
| 				<FormKeyValueView> | ||||
| 					<template #key>Outbox</template> | ||||
| 					<template #value><span class="_monospace">{{ ap.outbox }}</span></template> | ||||
| 				</FormKeyValueView> | ||||
| 			</FormGroup> | ||||
| 			<FormTextarea readonly tall code pre :value="ap.publicKey.publicKeyPem"> | ||||
| 				<span>Public Key</span> | ||||
| 			</FormTextarea> | ||||
| 			<FormKeyValueView> | ||||
| 				<template #key>Discoverable</template> | ||||
| 				<template #value>{{ ap.discoverable ? $ts.yes : $ts.no }}</template> | ||||
| 			</FormKeyValueView> | ||||
| 			<FormKeyValueView> | ||||
| 				<template #key>ManuallyApprovesFollowers</template> | ||||
| 				<template #value>{{ ap.manuallyApprovesFollowers ? $ts.yes : $ts.no }}</template> | ||||
| 			</FormKeyValueView> | ||||
| 			<FormObjectView tall :value="ap"> | ||||
| 				<span>Raw</span> | ||||
| 			</FormObjectView> | ||||
| 			<FormGroup> | ||||
| 				<FormLink :to="`https://${user.host}/.well-known/webfinger?resource=acct:${user.username}`" external>WebFinger</FormLink> | ||||
| 			</FormGroup> | ||||
| 			<FormLink v-if="user.host" :to="`/instance-info/${user.host}`">{{ $ts.instanceInfo }}<template #suffix>{{ user.host }}</template></FormLink> | ||||
| 			<FormKeyValueView v-else> | ||||
| 				<template #key>{{ $ts.instanceInfo }}</template> | ||||
| 				<template #value>(Local user)</template> | ||||
| 			</FormKeyValueView> | ||||
| 		</FormGroup> | ||||
| 	</FormSuspense> | ||||
| </FormBase> | ||||
| </template> | ||||
| 
 | ||||
| <script lang="ts"> | ||||
| import { defineAsyncComponent, defineComponent } from 'vue'; | ||||
| import FormObjectView from '@/components/debobigego/object-view.vue'; | ||||
| import FormTextarea from '@/components/debobigego/textarea.vue'; | ||||
| import FormLink from '@/components/debobigego/link.vue'; | ||||
| import FormBase from '@/components/debobigego/base.vue'; | ||||
| import FormGroup from '@/components/debobigego/group.vue'; | ||||
| import FormButton from '@/components/debobigego/button.vue'; | ||||
| import FormKeyValueView from '@/components/debobigego/key-value-view.vue'; | ||||
| import FormSuspense from '@/components/debobigego/suspense.vue'; | ||||
| import * as os from '@/os'; | ||||
| import number from '@/filters/number'; | ||||
| import bytes from '@/filters/bytes'; | ||||
| import * as symbols from '@/symbols'; | ||||
| import { url } from '@/config'; | ||||
| 
 | ||||
| export default defineComponent({ | ||||
| 	components: { | ||||
| 		FormBase, | ||||
| 		FormTextarea, | ||||
| 		FormObjectView, | ||||
| 		FormButton, | ||||
| 		FormLink, | ||||
| 		FormGroup, | ||||
| 		FormKeyValueView, | ||||
| 		FormSuspense, | ||||
| 	}, | ||||
| 
 | ||||
| 	props: { | ||||
| 		userId: { | ||||
| 			type: String, | ||||
| 			required: true | ||||
| 		} | ||||
| 	}, | ||||
| 
 | ||||
| 	data() { | ||||
| 		return { | ||||
| 			[symbols.PAGE_INFO]: { | ||||
| 				title: this.$ts.userInfo, | ||||
| 				icon: 'fas fa-info-circle' | ||||
| 			}, | ||||
| 			user: null, | ||||
| 			apPromiseFactory: null, | ||||
| 		} | ||||
| 	}, | ||||
| 
 | ||||
| 	mounted() { | ||||
| 		this.fetch(); | ||||
| 	}, | ||||
| 
 | ||||
| 	methods: { | ||||
| 		number, | ||||
| 		bytes, | ||||
| 
 | ||||
| 		async fetch() { | ||||
| 			this.user = await os.api('users/show', { | ||||
| 				userId: this.userId | ||||
| 			}); | ||||
| 
 | ||||
| 			this.apPromiseFactory = () => os.api('ap/get', { | ||||
| 				uri: this.user.uri || `${url}/users/${this.user.id}` | ||||
| 			}); | ||||
| 		} | ||||
| 	} | ||||
| }); | ||||
| </script> | ||||
|  | @ -1,70 +1,76 @@ | |||
| <template> | ||||
| <FormBase> | ||||
| <MkSpacer :content-max="500" :margin-min="16" :margin-max="32"> | ||||
| 	<FormSuspense :p="init"> | ||||
| 		<div class="_debobigegoItem aeakzknw"> | ||||
| 			<MkAvatar class="avatar" :user="user" :show-indicator="true"/> | ||||
| 		<div class="_formRoot"> | ||||
| 			<div class="_formBlock aeakzknw"> | ||||
| 				<MkAvatar class="avatar" :user="user" :show-indicator="true"/> | ||||
| 			</div> | ||||
| 
 | ||||
| 			<FormLink :to="userPage(user)">Profile</FormLink> | ||||
| 
 | ||||
| 			<div class="_formBlock"> | ||||
| 				<MkKeyValue :copy="acct(user)" oneline style="margin: 1em 0;"> | ||||
| 					<template #key>Acct</template> | ||||
| 					<template #value><span class="_monospace">{{ acct(user) }}</span></template> | ||||
| 				</MkKeyValue> | ||||
| 
 | ||||
| 				<MkKeyValue :copy="user.id" oneline style="margin: 1em 0;"> | ||||
| 					<template #key>ID</template> | ||||
| 					<template #value><span class="_monospace">{{ user.id }}</span></template> | ||||
| 				</MkKeyValue> | ||||
| 			</div> | ||||
| 
 | ||||
| 			<FormSection v-if="iAmModerator"> | ||||
| 				<template #label>Moderation</template> | ||||
| 				<FormSwitch v-if="user.host == null && $i.isAdmin && (moderator || !user.isAdmin)" v-model="moderator" class="_formBlock" @update:modelValue="toggleModerator">{{ $ts.moderator }}</FormSwitch> | ||||
| 				<FormSwitch v-model="silenced" class="_formBlock" @update:modelValue="toggleSilence">{{ $ts.silence }}</FormSwitch> | ||||
| 				<FormSwitch v-model="suspended" class="_formBlock" @update:modelValue="toggleSuspend">{{ $ts.suspend }}</FormSwitch> | ||||
| 				<FormButton v-if="user.host == null && iAmModerator" class="_formBlock" @click="resetPassword"><i class="fas fa-key"></i> {{ $ts.resetPassword }}</FormButton> | ||||
| 			</FormSection> | ||||
| 
 | ||||
| 			<FormSection> | ||||
| 				<template #label>ActivityPub</template> | ||||
| 
 | ||||
| 				<div class="_formBlock"> | ||||
| 					<MkKeyValue v-if="user.host" oneline style="margin: 1em 0;"> | ||||
| 						<template #key>{{ $ts.instanceInfo }}</template> | ||||
| 						<template #value><MkA :to="`/instance-info/${user.host}`" class="_link">{{ user.host }} <i class="fas fa-angle-right"></i></MkA></template> | ||||
| 					</MkKeyValue> | ||||
| 					<MkKeyValue v-else oneline style="margin: 1em 0;"> | ||||
| 						<template #key>{{ $ts.instanceInfo }}</template> | ||||
| 						<template #value>(Local user)</template> | ||||
| 					</MkKeyValue> | ||||
| 					<MkKeyValue oneline style="margin: 1em 0;"> | ||||
| 						<template #key>{{ $ts.updatedAt }}</template> | ||||
| 						<template #value><MkTime v-if="user.lastFetchedAt" mode="detail" :time="user.lastFetchedAt"/><span v-else>N/A</span></template> | ||||
| 					</MkKeyValue> | ||||
| 					<MkKeyValue v-if="ap" oneline style="margin: 1em 0;"> | ||||
| 						<template #key>Type</template> | ||||
| 						<template #value><span class="_monospace">{{ ap.type }}</span></template> | ||||
| 					</MkKeyValue> | ||||
| 				</div> | ||||
| 
 | ||||
| 				<FormButton v-if="user.host != null" class="_formBlock" @click="updateRemoteUser"><i class="fas fa-sync"></i> {{ $ts.updateRemoteUser }}</FormButton> | ||||
| 			</FormSection> | ||||
| 
 | ||||
| 			<FormObjectView tall :value="user"> | ||||
| 				<span>Raw</span> | ||||
| 			</FormObjectView> | ||||
| 		</div> | ||||
| 
 | ||||
| 		<FormLink :to="userPage(user)">Profile</FormLink> | ||||
| 
 | ||||
| 		<FormGroup> | ||||
| 			<FormKeyValueView> | ||||
| 				<template #key>Acct</template> | ||||
| 				<template #value><span class="_monospace">{{ acct(user) }}</span></template> | ||||
| 			</FormKeyValueView> | ||||
| 
 | ||||
| 			<FormKeyValueView> | ||||
| 				<template #key>ID</template> | ||||
| 				<template #value><span class="_monospace">{{ user.id }}</span></template> | ||||
| 			</FormKeyValueView> | ||||
| 		</FormGroup> | ||||
| 
 | ||||
| 		<FormGroup v-if="iAmModerator"> | ||||
| 			<FormSwitch v-if="user.host == null && $i.isAdmin && (moderator || !user.isAdmin)" v-model="moderator" @update:modelValue="toggleModerator">{{ $ts.moderator }}</FormSwitch> | ||||
| 			<FormSwitch v-model="silenced" @update:modelValue="toggleSilence">{{ $ts.silence }}</FormSwitch> | ||||
| 			<FormSwitch v-model="suspended" @update:modelValue="toggleSuspend">{{ $ts.suspend }}</FormSwitch> | ||||
| 		</FormGroup> | ||||
| 
 | ||||
| 		<FormGroup> | ||||
| 			<FormButton v-if="user.host != null" @click="updateRemoteUser"><i class="fas fa-sync"></i> {{ $ts.updateRemoteUser }}</FormButton> | ||||
| 			<FormButton v-if="user.host == null && iAmModerator" @click="resetPassword"><i class="fas fa-key"></i> {{ $ts.resetPassword }}</FormButton> | ||||
| 		</FormGroup> | ||||
| 
 | ||||
| 		<FormGroup> | ||||
| 			<FormLink :to="`/user-ap-info/${user.id}`">ActivityPub</FormLink> | ||||
| 
 | ||||
| 			<FormLink v-if="user.host" :to="`/instance-info/${user.host}`">{{ $ts.instanceInfo }}<template #suffix>{{ user.host }}</template></FormLink> | ||||
| 			<FormKeyValueView v-else> | ||||
| 				<template #key>{{ $ts.instanceInfo }}</template> | ||||
| 				<template #value>(Local user)</template> | ||||
| 			</FormKeyValueView> | ||||
| 		</FormGroup> | ||||
| 
 | ||||
| 		<FormGroup> | ||||
| 			<FormKeyValueView> | ||||
| 				<template #key>{{ $ts.updatedAt }}</template> | ||||
| 				<template #value><MkTime v-if="user.lastFetchedAt" mode="detail" :time="user.lastFetchedAt"/><span v-else>N/A</span></template> | ||||
| 			</FormKeyValueView> | ||||
| 		</FormGroup> | ||||
| 
 | ||||
| 		<FormObjectView tall :value="user"> | ||||
| 			<span>Raw</span> | ||||
| 		</FormObjectView> | ||||
| 	</FormSuspense> | ||||
| </FormBase> | ||||
| </MkSpacer> | ||||
| </template> | ||||
| 
 | ||||
| <script lang="ts"> | ||||
| import { computed, defineAsyncComponent, defineComponent } from 'vue'; | ||||
| import FormObjectView from '@/components/debobigego/object-view.vue'; | ||||
| import FormTextarea from '@/components/debobigego/textarea.vue'; | ||||
| import FormSwitch from '@/components/debobigego/switch.vue'; | ||||
| import FormLink from '@/components/debobigego/link.vue'; | ||||
| import FormBase from '@/components/debobigego/base.vue'; | ||||
| import FormGroup from '@/components/debobigego/group.vue'; | ||||
| import FormButton from '@/components/debobigego/button.vue'; | ||||
| import FormKeyValueView from '@/components/debobigego/key-value-view.vue'; | ||||
| import FormSuspense from '@/components/debobigego/suspense.vue'; | ||||
| import FormTextarea from '@/components/form/textarea.vue'; | ||||
| import FormSwitch from '@/components/form/switch.vue'; | ||||
| import FormLink from '@/components/form/link.vue'; | ||||
| import FormSection from '@/components/form/section.vue'; | ||||
| import FormButton from '@/components/ui/button.vue'; | ||||
| import MkKeyValue from '@/components/key-value.vue'; | ||||
| import FormSuspense from '@/components/form/suspense.vue'; | ||||
| import * as os from '@/os'; | ||||
| import number from '@/filters/number'; | ||||
| import bytes from '@/filters/bytes'; | ||||
|  | @ -74,14 +80,13 @@ import { userPage, acct } from '@/filters/user'; | |||
| 
 | ||||
| export default defineComponent({ | ||||
| 	components: { | ||||
| 		FormBase, | ||||
| 		FormSection, | ||||
| 		FormTextarea, | ||||
| 		FormSwitch, | ||||
| 		FormObjectView, | ||||
| 		FormButton, | ||||
| 		FormLink, | ||||
| 		FormGroup, | ||||
| 		FormKeyValueView, | ||||
| 		MkKeyValue, | ||||
| 		FormSuspense, | ||||
| 	}, | ||||
| 
 | ||||
|  | @ -97,6 +102,7 @@ export default defineComponent({ | |||
| 			[symbols.PAGE_INFO]: computed(() => ({ | ||||
| 				title: this.user ? acct(this.user) : this.$ts.userInfo, | ||||
| 				icon: 'fas fa-info-circle', | ||||
| 				bg: 'var(--bg)', | ||||
| 				actions: this.user ? [this.user.url ? { | ||||
| 					text: this.user.url, | ||||
| 					icon: 'fas fa-external-link-alt', | ||||
|  | @ -108,6 +114,7 @@ export default defineComponent({ | |||
| 			init: null, | ||||
| 			user: null, | ||||
| 			info: null, | ||||
| 			ap: null, | ||||
| 			moderator: false, | ||||
| 			silenced: false, | ||||
| 			suspended: false, | ||||
|  | @ -126,6 +133,13 @@ export default defineComponent({ | |||
| 				this.init = this.createFetcher(); | ||||
| 			}, | ||||
| 			immediate: true | ||||
| 		}, | ||||
| 		user() { | ||||
| 			os.api('ap/get', { | ||||
| 				uri: this.user.uri || `${url}/users/${this.user.id}` | ||||
| 			}).then(res => { | ||||
| 				this.ap = res; | ||||
| 			}); | ||||
| 		} | ||||
| 	}, | ||||
| 
 | ||||
|  | @ -234,7 +248,6 @@ export default defineComponent({ | |||
| .aeakzknw { | ||||
| 	> .avatar { | ||||
| 		display: block; | ||||
| 		margin: 0 auto; | ||||
| 		width: 64px; | ||||
| 		height: 64px; | ||||
| 	} | ||||
|  |  | |||
|  | @ -73,7 +73,6 @@ const defaultRoutes = [ | |||
| 	{ path: '/notes/:note', name: 'note', component: page('note'), props: route => ({ noteId: route.params.note }) }, | ||||
| 	{ path: '/tags/:tag', component: page('tag'), props: route => ({ tag: route.params.tag }) }, | ||||
| 	{ path: '/user-info/:user', component: page('user-info'), props: route => ({ userId: route.params.user }) }, | ||||
| 	{ path: '/user-ap-info/:user', component: page('user-ap-info'), props: route => ({ userId: route.params.user }) }, | ||||
| 	{ path: '/instance-info/:host', component: page('instance-info'), props: route => ({ host: route.params.host }) }, | ||||
| 	{ path: '/games/reversi', component: page('reversi/index') }, | ||||
| 	{ path: '/games/reversi/:gameId', component: page('reversi/game'), props: route => ({ gameId: route.params.gameId }) }, | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue