mirror of
https://github.com/misskey-dev/misskey.git
synced 2024-12-22 21:45:13 +01:00
parent
b8aad35009
commit
e88ce1746d
7 changed files with 257 additions and 17 deletions
|
@ -519,6 +519,14 @@ common/views/components/profile-editor.vue:
|
|||
email-verified: "メールアドレスが確認されました"
|
||||
email-not-verified: "メールアドレスが確認されていません。メールボックスをご確認ください。"
|
||||
|
||||
common/views/components/user-list-editor.vue:
|
||||
users: "ユーザー"
|
||||
rename: "リスト名を変更"
|
||||
delete: "リストを削除"
|
||||
remove-user: "このリストから削除"
|
||||
delete-are-you-sure: "リスト「$1」を削除しますか?"
|
||||
deleted: "削除しました"
|
||||
|
||||
common/views/widgets/broadcast.vue:
|
||||
fetching: "確認中"
|
||||
no-broadcasts: "お知らせはありません"
|
||||
|
|
150
src/client/app/common/views/components/user-list-editor.vue
Normal file
150
src/client/app/common/views/components/user-list-editor.vue
Normal file
|
@ -0,0 +1,150 @@
|
|||
<template>
|
||||
<div class="cudqjmnl">
|
||||
<ui-card>
|
||||
<div slot="title"><fa :icon="faList"/> {{ list.title }}</div>
|
||||
|
||||
<section>
|
||||
<ui-button @click="rename"><fa :icon="faICursor"/> {{ $t('rename') }}</ui-button>
|
||||
<ui-button @click="del"><fa :icon="faTrashAlt"/> {{ $t('delete') }}</ui-button>
|
||||
</section>
|
||||
</ui-card>
|
||||
|
||||
<ui-card>
|
||||
<div slot="title"><fa :icon="faUsers"/> {{ $t('users') }}</div>
|
||||
|
||||
<section>
|
||||
<sequential-entrance animation="entranceFromTop" delay="25">
|
||||
<div class="phcqulfl" v-for="user in users">
|
||||
<div>
|
||||
<a :href="user | userPage">
|
||||
<mk-avatar class="avatar" :user="user" :disable-link="true"/>
|
||||
</a>
|
||||
</div>
|
||||
<div>
|
||||
<header>
|
||||
<b><mk-user-name :user="user"/></b>
|
||||
<span class="username">@{{ user | acct }}</span>
|
||||
</header>
|
||||
<div>
|
||||
<a @click="remove(user)">{{ $t('remove-user') }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</sequential-entrance>
|
||||
</section>
|
||||
</ui-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import i18n from '../../../i18n';
|
||||
import { faList, faICursor, faUsers } from '@fortawesome/free-solid-svg-icons';
|
||||
import { faTrashAlt } from '@fortawesome/free-regular-svg-icons';
|
||||
|
||||
export default Vue.extend({
|
||||
i18n: i18n('common/views/components/user-list-editor.vue'),
|
||||
|
||||
props: {
|
||||
list: {
|
||||
required: true
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
users: [],
|
||||
faList, faICursor, faTrashAlt, faUsers
|
||||
};
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.fetchUsers();
|
||||
},
|
||||
|
||||
methods: {
|
||||
fetchUsers() {
|
||||
this.$root.api('users/show', {
|
||||
userIds: this.list.userIds
|
||||
}).then(users => {
|
||||
this.users = users;
|
||||
});
|
||||
},
|
||||
|
||||
rename() {
|
||||
this.$root.dialog({
|
||||
title: this.$t('rename'),
|
||||
input: {
|
||||
default: this.list.title
|
||||
}
|
||||
}).then(({ canceled, result: title }) => {
|
||||
if (canceled) return;
|
||||
this.$root.api('users/lists/update', {
|
||||
listId: this.list.id,
|
||||
title: title
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
del() {
|
||||
this.$root.dialog({
|
||||
type: 'warning',
|
||||
text: this.$t('delete-are-you-sure').replace('$1', this.list.title),
|
||||
showCancelButton: true
|
||||
}).then(({ canceled }) => {
|
||||
if (canceled) return;
|
||||
|
||||
this.$root.api('users/lists/delete', {
|
||||
listId: this.list.id
|
||||
}).then(() => {
|
||||
this.$root.dialog({
|
||||
type: 'success',
|
||||
text: this.$t('deleted')
|
||||
});
|
||||
}).catch(e => {
|
||||
this.$root.dialog({
|
||||
type: 'error',
|
||||
text: e
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
remove(user: any) {
|
||||
this.$root.api('users/lists/pull', {
|
||||
listId: this.list.id,
|
||||
userId: user.id
|
||||
}).then(() => {
|
||||
this.fetchUsers();
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
.cudqjmnl
|
||||
.phcqulfl
|
||||
display flex
|
||||
padding 16px 0
|
||||
border-top solid 1px var(--faceDivider)
|
||||
|
||||
> div:first-child
|
||||
> a
|
||||
> .avatar
|
||||
width 64px
|
||||
height 64px
|
||||
|
||||
> div:last-child
|
||||
flex 1
|
||||
padding-left 16px
|
||||
|
||||
@media (max-width 500px)
|
||||
font-size 14px
|
||||
|
||||
> header
|
||||
> .username
|
||||
margin-left 8px
|
||||
opacity 0.7
|
||||
|
||||
</style>
|
|
@ -92,6 +92,7 @@
|
|||
import Vue from 'vue';
|
||||
import i18n from '../../../i18n';
|
||||
import MkUserListsWindow from './user-lists-window.vue';
|
||||
import MkUserListWindow from './user-list-window.vue';
|
||||
import MkFollowRequestsWindow from './received-follow-requests-window.vue';
|
||||
import MkSettingsWindow from './settings-window.vue';
|
||||
import MkDriveWindow from './drive-window.vue';
|
||||
|
@ -143,7 +144,9 @@ export default Vue.extend({
|
|||
this.close();
|
||||
const w = this.$root.new(MkUserListsWindow);
|
||||
w.$once('choosen', list => {
|
||||
this.$router.push(`i/lists/${ list.id }`);
|
||||
this.$root.new(MkUserListWindow, {
|
||||
list
|
||||
});
|
||||
});
|
||||
},
|
||||
followRequests() {
|
||||
|
|
24
src/client/app/desktop/views/components/user-list-window.vue
Normal file
24
src/client/app/desktop/views/components/user-list-window.vue
Normal file
|
@ -0,0 +1,24 @@
|
|||
<template>
|
||||
<mk-window ref="window" width="450px" height="500px" @closed="destroyDom">
|
||||
<span slot="header"><fa icon="list"/> {{ list.title }}</span>
|
||||
|
||||
<x-editor :list="list"/>
|
||||
</mk-window>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import XEditor from '../../../common/views/components/user-list-editor.vue';
|
||||
|
||||
export default Vue.extend({
|
||||
components: {
|
||||
XEditor
|
||||
},
|
||||
|
||||
props: {
|
||||
list: {
|
||||
required: true
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<mk-window ref="window" is-modal width="450px" height="500px" @closed="destroyDom">
|
||||
<mk-window ref="window" width="450px" height="500px" @closed="destroyDom">
|
||||
<span slot="header"><fa icon="list"/> {{ $t('title') }}</span>
|
||||
|
||||
<div class="xkxvokkjlptzyewouewmceqcxhpgzprp">
|
||||
|
|
|
@ -3,11 +3,7 @@
|
|||
<span slot="header" v-if="!fetching"><fa icon="list"/>{{ list.title }}</span>
|
||||
|
||||
<main v-if="!fetching">
|
||||
<ul>
|
||||
<li v-for="user in users" :key="user.id"><router-link :to="user | userPage">
|
||||
<mk-user-name :user="user"/>
|
||||
</router-link></li>
|
||||
</ul>
|
||||
<x-editor :list="list"/>
|
||||
</main>
|
||||
</mk-ui>
|
||||
</template>
|
||||
|
@ -15,13 +11,16 @@
|
|||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import Progress from '../../../common/scripts/loading';
|
||||
import XEditor from '../../../common/views/components/user-list-editor.vue';
|
||||
|
||||
export default Vue.extend({
|
||||
components: {
|
||||
XEditor
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
fetching: true,
|
||||
list: null,
|
||||
users: null
|
||||
list: null
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
|
@ -42,12 +41,6 @@ export default Vue.extend({
|
|||
this.fetching = false;
|
||||
|
||||
Progress.done();
|
||||
|
||||
this.$root.api('users/show', {
|
||||
userIds: this.list.userIds
|
||||
}).then(users => {
|
||||
this.users = users;
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -55,8 +48,6 @@ export default Vue.extend({
|
|||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
|
||||
|
||||
main
|
||||
width 100%
|
||||
max-width 680px
|
||||
|
|
64
src/server/api/endpoints/users/lists/pull.ts
Normal file
64
src/server/api/endpoints/users/lists/pull.ts
Normal file
|
@ -0,0 +1,64 @@
|
|||
import $ from 'cafy'; import ID, { transform } from '../../../../../misc/cafy-id';
|
||||
import UserList from '../../../../../models/user-list';
|
||||
import User, { pack as packUser } from '../../../../../models/user';
|
||||
import { publishUserListStream } from '../../../../../stream';
|
||||
import define from '../../../define';
|
||||
|
||||
export const meta = {
|
||||
desc: {
|
||||
'ja-JP': '指定したユーザーリストから指定したユーザーを削除します。',
|
||||
'en-US': 'Remove a user to a user list.'
|
||||
},
|
||||
|
||||
requireCredential: true,
|
||||
|
||||
kind: 'account-write',
|
||||
|
||||
params: {
|
||||
listId: {
|
||||
validator: $.type(ID),
|
||||
transform: transform,
|
||||
},
|
||||
|
||||
userId: {
|
||||
validator: $.type(ID),
|
||||
transform: transform,
|
||||
desc: {
|
||||
'ja-JP': '対象のユーザーのID',
|
||||
'en-US': 'Target user ID'
|
||||
}
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
export default define(meta, (ps, me) => new Promise(async (res, rej) => {
|
||||
// Fetch the list
|
||||
const userList = await UserList.findOne({
|
||||
_id: ps.listId,
|
||||
userId: me._id,
|
||||
});
|
||||
|
||||
if (userList == null) {
|
||||
return rej('list not found');
|
||||
}
|
||||
|
||||
// Fetch the user
|
||||
const user = await User.findOne({
|
||||
_id: ps.userId
|
||||
});
|
||||
|
||||
if (user == null) {
|
||||
return rej('user not found');
|
||||
}
|
||||
|
||||
// Push the user
|
||||
await UserList.update({ _id: userList._id }, {
|
||||
$pull: {
|
||||
userIds: user._id
|
||||
}
|
||||
});
|
||||
|
||||
res();
|
||||
|
||||
publishUserListStream(userList._id, 'userRemoved', await packUser(user));
|
||||
}));
|
Loading…
Reference in a new issue