diff --git a/src/client/components/form/suspense.vue b/src/client/components/form/suspense.vue
index 2a48faccb..d04dc0762 100644
--- a/src/client/components/form/suspense.vue
+++ b/src/client/components/form/suspense.vue
@@ -9,9 +9,9 @@
-
@@ -20,8 +20,13 @@
-
-
diff --git a/src/client/pages/instance/users.vue b/src/client/pages/instance/users.vue
index 452886abd..2808b70fb 100644
--- a/src/client/pages/instance/users.vue
+++ b/src/client/pages/instance/users.vue
@@ -162,7 +162,7 @@ export default defineComponent({
},
show(user) {
- os.pageWindow(`/instance/user/${user.id}`);
+ os.pageWindow(`/user-info/${user.id}`);
},
acct
diff --git a/src/client/pages/user-info.vue b/src/client/pages/user-info.vue
index 378fbb7b5..090e8cdda 100644
--- a/src/client/pages/user-info.vue
+++ b/src/client/pages/user-info.vue
@@ -1,35 +1,55 @@
+
+
+
+
+ Profile
+
-
+
+ Acct
+ {{ acct(user) }}
+
ID
{{ user.id }}
-
-
- ActivityPub
-
- {{ $ts.instanceInfo }}{{ user.host }}
-
- {{ $ts.instanceInfo }}
- (Local user)
-
-
-
-
-
- {{ $ts.updatedAt }}
- N/A
-
-
-
-
- Raw
-
+
+
+ {{ $ts.moderator }}
+ {{ $ts.silence }}
+ {{ $ts.suspend }}
+
+
+
+ {{ $ts.updateRemoteUser }}
+ {{ $ts.resetPassword }}
+
+
+
+ ActivityPub
+
+ {{ $ts.instanceInfo }}{{ user.host }}
+
+ {{ $ts.instanceInfo }}
+ (Local user)
+
+
+
+
+
+ {{ $ts.updatedAt }}
+ N/A
+
+
+
+
+ Raw
+
@@ -38,6 +58,7 @@
import { computed, defineAsyncComponent, defineComponent } from 'vue';
import FormObjectView from '@client/components/form/object-view.vue';
import FormTextarea from '@client/components/form/textarea.vue';
+import FormSwitch from '@client/components/form/switch.vue';
import FormLink from '@client/components/form/link.vue';
import FormBase from '@client/components/form/base.vue';
import FormGroup from '@client/components/form/group.vue';
@@ -49,11 +70,13 @@ import number from '@client/filters/number';
import bytes from '@client/filters/bytes';
import * as symbols from '@client/symbols';
import { url } from '@client/config';
+import { userPage, acct } from '@client/filters/user';
export default defineComponent({
components: {
FormBase,
FormTextarea,
+ FormSwitch,
FormObjectView,
FormButton,
FormLink,
@@ -72,7 +95,7 @@ export default defineComponent({
data() {
return {
[symbols.PAGE_INFO]: computed(() => ({
- title: this.$ts.userInfo,
+ title: this.user ? acct(this.user) : this.$ts.userInfo,
icon: 'fas fa-info-circle',
actions: this.user ? [this.user.url ? {
text: this.user.url,
@@ -84,17 +107,23 @@ export default defineComponent({
})),
init: null,
user: null,
+ info: null,
+ moderator: false,
+ silenced: false,
+ suspended: false,
+ }
+ },
+
+ computed: {
+ iAmModerator(): boolean {
+ return this.$i && (this.$i.isAdmin || this.$i.isModerator);
}
},
watch: {
userId: {
handler() {
- this.init = () => os.api('users/show', {
- userId: this.userId
- }).then(user => {
- this.user = user;
- });
+ this.init = this.createFetcher();
},
immediate: true
}
@@ -103,6 +132,114 @@ export default defineComponent({
methods: {
number,
bytes,
+ userPage,
+ acct,
+
+ createFetcher() {
+ if (this.iAmModerator) {
+ return () => Promise.all([os.api('users/show', {
+ userId: this.userId
+ }), os.api('admin/show-user', {
+ userId: this.userId
+ })]).then(([user, info]) => {
+ this.user = user;
+ this.info = info;
+ this.moderator = this.info.isModerator;
+ this.silenced = this.info.isSilenced;
+ this.suspended = this.info.isSuspended;
+ });
+ } else {
+ return () => os.api('users/show', {
+ userId: this.userId
+ }).then((user) => {
+ this.user = user;
+ });
+ }
+ },
+
+ refreshUser() {
+ this.init = this.createFetcher();
+ },
+
+ async updateRemoteUser() {
+ await os.apiWithDialog('admin/update-remote-user', { userId: this.user.id });
+ this.refreshUser();
+ },
+
+ async resetPassword() {
+ os.apiWithDialog('admin/reset-password', {
+ userId: this.user.id,
+ }, undefined, ({ password }) => {
+ os.dialog({
+ type: 'success',
+ text: this.$t('newPasswordIs', { password })
+ });
+ });
+ },
+
+ async toggleSilence(v) {
+ const confirm = await os.dialog({
+ type: 'warning',
+ showCancelButton: true,
+ text: v ? this.$ts.silenceConfirm : this.$ts.unsilenceConfirm,
+ });
+ if (confirm.canceled) {
+ this.silenced = !v;
+ } else {
+ await os.api(v ? 'admin/silence-user' : 'admin/unsilence-user', { userId: this.user.id });
+ await this.refreshUser();
+ }
+ },
+
+ async toggleSuspend(v) {
+ const confirm = await os.dialog({
+ type: 'warning',
+ showCancelButton: true,
+ text: v ? this.$ts.suspendConfirm : this.$ts.unsuspendConfirm,
+ });
+ if (confirm.canceled) {
+ this.suspended = !v;
+ } else {
+ await os.api(v ? 'admin/suspend-user' : 'admin/unsuspend-user', { userId: this.user.id });
+ await this.refreshUser();
+ }
+ },
+
+ async toggleModerator(v) {
+ await os.api(v ? 'admin/moderators/add' : 'admin/moderators/remove', { userId: this.user.id });
+ await this.refreshUser();
+ },
+
+ async deleteAllFiles() {
+ const confirm = await os.dialog({
+ type: 'warning',
+ showCancelButton: true,
+ text: this.$ts.deleteAllFilesConfirm,
+ });
+ if (confirm.canceled) return;
+ const process = async () => {
+ await os.api('admin/delete-all-files-of-a-user', { userId: this.user.id });
+ os.success();
+ };
+ await process().catch(e => {
+ os.dialog({
+ type: 'error',
+ text: e.toString()
+ });
+ });
+ await this.refreshUser();
+ },
}
});
+
+
diff --git a/src/client/router.ts b/src/client/router.ts
index 93de287ea..26a4dac49 100644
--- a/src/client/router.ts
+++ b/src/client/router.ts
@@ -59,7 +59,6 @@ export const router = createRouter({
{ path: '/my/antennas', component: page('my-antennas/index') },
{ path: '/my/clips', component: page('my-clips/index') },
{ path: '/scratchpad', component: page('scratchpad') },
- { path: '/instance/user/:user', component: page('instance/user'), props: route => ({ userId: route.params.user }) },
{ path: '/instance/:page(.*)?', component: page('instance/index'), props: route => ({ initialPage: route.params.page || null }) },
{ path: '/instance', component: page('instance/index') },
{ path: '/notes/:note', name: 'note', component: page('note'), props: route => ({ noteId: route.params.note }) },
diff --git a/src/client/scripts/get-user-menu.ts b/src/client/scripts/get-user-menu.ts
index 9a003b5c3..ceb2bfe17 100644
--- a/src/client/scripts/get-user-menu.ts
+++ b/src/client/scripts/get-user-menu.ts
@@ -124,13 +124,7 @@ export function getUserMenu(user) {
action: () => {
copyToClipboard(`@${user.username}@${user.host || host}`);
}
- }, ($i && ($i.isAdmin || $i.isModerator)) ? {
- icon: 'fas fa-info-circle',
- text: i18n.locale.info,
- action: () => {
- os.pageWindow(`/instance/user/${user.id}`);
- }
- } : {
+ }, {
icon: 'fas fa-info-circle',
text: i18n.locale.info,
action: () => {
diff --git a/src/client/scripts/lookup-user.ts b/src/client/scripts/lookup-user.ts
index 1bcfd8e9d..269777d87 100644
--- a/src/client/scripts/lookup-user.ts
+++ b/src/client/scripts/lookup-user.ts
@@ -10,7 +10,7 @@ export async function lookupUser() {
if (canceled) return;
const show = (user) => {
- os.pageWindow(`/instance/user/${user.id}`);
+ os.pageWindow(`/user-info/${user.id}`);
};
const usernamePromise = os.api('users/show', parseAcct(result));