summaryrefslogtreecommitdiff
path: root/packages/client
diff options
context:
space:
mode:
Diffstat (limited to 'packages/client')
-rw-r--r--packages/client/src/components/chart.vue8
-rw-r--r--packages/client/src/components/form/section.vue7
-rw-r--r--packages/client/src/components/form/switch.vue2
-rw-r--r--packages/client/src/components/global/spacer.vue2
-rw-r--r--packages/client/src/components/instance-stats.vue15
-rw-r--r--packages/client/src/components/key-value.vue27
-rw-r--r--packages/client/src/pages/about.vue3
-rw-r--r--packages/client/src/pages/admin/instance.vue291
-rw-r--r--packages/client/src/pages/admin/metrics.vue1
-rw-r--r--packages/client/src/pages/admin/overview.vue7
-rw-r--r--packages/client/src/pages/instance-info.vue230
-rw-r--r--packages/client/src/pages/user-ap-info.vue124
-rw-r--r--packages/client/src/pages/user-info.vue123
-rw-r--r--packages/client/src/router.ts1
14 files changed, 224 insertions, 617 deletions
diff --git a/packages/client/src/components/chart.vue b/packages/client/src/components/chart.vue
index c4d0eb85dd..1959271f5d 100644
--- a/packages/client/src/components/chart.vue
+++ b/packages/client/src/components/chart.vue
@@ -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: {
diff --git a/packages/client/src/components/form/section.vue b/packages/client/src/components/form/section.vue
index ab9fbe5fcd..c6e34ef1cc 100644
--- a/packages/client/src/components/form/section.vue
+++ b/packages/client/src/components/form/section.vue
@@ -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>
diff --git a/packages/client/src/components/form/switch.vue b/packages/client/src/components/form/switch.vue
index ac3284e7da..f8a07b4caa 100644
--- a/packages/client/src/components/form/switch.vue
+++ b/packages/client/src/components/form/switch.vue
@@ -111,7 +111,7 @@ export default defineComponent({
}
> .label {
- margin-left: 16px;
+ margin-left: 12px;
margin-top: 2px;
display: block;
transition: inherit;
diff --git a/packages/client/src/components/global/spacer.vue b/packages/client/src/components/global/spacer.vue
index e2f1d1aec7..8a1d7a4e8a 100644
--- a/packages/client/src/components/global/spacer.vue
+++ b/packages/client/src/components/global/spacer.vue
@@ -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;
diff --git a/packages/client/src/components/instance-stats.vue b/packages/client/src/components/instance-stats.vue
index bc62998a4a..409c3a49ca 100644
--- a/packages/client/src/components/instance-stats.vue
+++ b/packages/client/src/components/instance-stats.vue
@@ -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>
diff --git a/packages/client/src/components/key-value.vue b/packages/client/src/components/key-value.vue
index 6a9a948ce9..da98abd77c 100644
--- a/packages/client/src/components/key-value.vue
+++ b/packages/client/src/components/key-value.vue
@@ -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>
diff --git a/packages/client/src/pages/about.vue b/packages/client/src/pages/about.vue
index 618e569839..e9d17feec1 100644
--- a/packages/client/src/pages/about.vue
+++ b/packages/client/src/pages/about.vue
@@ -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,
diff --git a/packages/client/src/pages/admin/instance.vue b/packages/client/src/pages/admin/instance.vue
deleted file mode 100644
index 51fcb8675a..0000000000
--- a/packages/client/src/pages/admin/instance.vue
+++ /dev/null
@@ -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>
diff --git a/packages/client/src/pages/admin/metrics.vue b/packages/client/src/pages/admin/metrics.vue
index f566061ceb..1de297fd93 100644
--- a/packages/client/src/pages/admin/metrics.vue
+++ b/packages/client/src/pages/admin/metrics.vue
@@ -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,
diff --git a/packages/client/src/pages/admin/overview.vue b/packages/client/src/pages/admin/overview.vue
index 3dcfce01e5..564a63fda0 100644
--- a/packages/client/src/pages/admin/overview.vue
+++ b/packages/client/src/pages/admin/overview.vue
@@ -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,
diff --git a/packages/client/src/pages/instance-info.vue b/packages/client/src/pages/instance-info.vue
index 85096d991a..d6d72f6601 100644
--- a/packages/client/src/pages/instance-info.vue
+++ b/packages/client/src/pages/instance-info.vue
@@ -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>
-
- <FormButton v-if="$i.isAdmin || $i.isModerator" primary @click="info">{{ $ts.settings }}</FormButton>
+<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>
- <FormTextarea readonly :value="instance.description">
- <span>{{ $ts.description }}</span>
- </FormTextarea>
+ <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>
- <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>
diff --git a/packages/client/src/pages/user-ap-info.vue b/packages/client/src/pages/user-ap-info.vue
deleted file mode 100644
index 0027381f53..0000000000
--- a/packages/client/src/pages/user-ap-info.vue
+++ /dev/null
@@ -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>
diff --git a/packages/client/src/pages/user-info.vue b/packages/client/src/pages/user-info.vue
index 0fd208a64a..e3c10c6df9 100644
--- a/packages/client/src/pages/user-info.vue
+++ b/packages/client/src/pages/user-info.vue
@@ -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>
-
- <FormLink :to="userPage(user)">Profile</FormLink>
+ <div class="_formRoot">
+ <div class="_formBlock aeakzknw">
+ <MkAvatar class="avatar" :user="user" :show-indicator="true"/>
+ </div>
- <FormGroup>
- <FormKeyValueView>
- <template #key>Acct</template>
- <template #value><span class="_monospace">{{ acct(user) }}</span></template>
- </FormKeyValueView>
+ <FormLink :to="userPage(user)">Profile</FormLink>
- <FormKeyValueView>
- <template #key>ID</template>
- <template #value><span class="_monospace">{{ user.id }}</span></template>
- </FormKeyValueView>
- </FormGroup>
+ <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>
- <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>
+ <MkKeyValue :copy="user.id" oneline style="margin: 1em 0;">
+ <template #key>ID</template>
+ <template #value><span class="_monospace">{{ user.id }}</span></template>
+ </MkKeyValue>
+ </div>
- <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>
+ <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>
- <FormGroup>
- <FormLink :to="`/user-ap-info/${user.id}`">ActivityPub</FormLink>
+ <FormSection>
+ <template #label>ActivityPub</template>
- <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>
+ <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>
- <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>
+ <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>
+ <FormObjectView tall :value="user">
+ <span>Raw</span>
+ </FormObjectView>
+ </div>
</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;
}
diff --git a/packages/client/src/router.ts b/packages/client/src/router.ts
index 9b4dd162f3..2a07ff37cd 100644
--- a/packages/client/src/router.ts
+++ b/packages/client/src/router.ts
@@ -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 }) },