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-verified: "メールアドレスが確認されました"
|
||||||
email-not-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:
|
common/views/widgets/broadcast.vue:
|
||||||
fetching: "確認中"
|
fetching: "確認中"
|
||||||
no-broadcasts: "お知らせはありません"
|
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 Vue from 'vue';
|
||||||
import i18n from '../../../i18n';
|
import i18n from '../../../i18n';
|
||||||
import MkUserListsWindow from './user-lists-window.vue';
|
import MkUserListsWindow from './user-lists-window.vue';
|
||||||
|
import MkUserListWindow from './user-list-window.vue';
|
||||||
import MkFollowRequestsWindow from './received-follow-requests-window.vue';
|
import MkFollowRequestsWindow from './received-follow-requests-window.vue';
|
||||||
import MkSettingsWindow from './settings-window.vue';
|
import MkSettingsWindow from './settings-window.vue';
|
||||||
import MkDriveWindow from './drive-window.vue';
|
import MkDriveWindow from './drive-window.vue';
|
||||||
|
@ -143,7 +144,9 @@ export default Vue.extend({
|
||||||
this.close();
|
this.close();
|
||||||
const w = this.$root.new(MkUserListsWindow);
|
const w = this.$root.new(MkUserListsWindow);
|
||||||
w.$once('choosen', list => {
|
w.$once('choosen', list => {
|
||||||
this.$router.push(`i/lists/${ list.id }`);
|
this.$root.new(MkUserListWindow, {
|
||||||
|
list
|
||||||
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
followRequests() {
|
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>
|
<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>
|
<span slot="header"><fa icon="list"/> {{ $t('title') }}</span>
|
||||||
|
|
||||||
<div class="xkxvokkjlptzyewouewmceqcxhpgzprp">
|
<div class="xkxvokkjlptzyewouewmceqcxhpgzprp">
|
||||||
|
|
|
@ -3,11 +3,7 @@
|
||||||
<span slot="header" v-if="!fetching"><fa icon="list"/>{{ list.title }}</span>
|
<span slot="header" v-if="!fetching"><fa icon="list"/>{{ list.title }}</span>
|
||||||
|
|
||||||
<main v-if="!fetching">
|
<main v-if="!fetching">
|
||||||
<ul>
|
<x-editor :list="list"/>
|
||||||
<li v-for="user in users" :key="user.id"><router-link :to="user | userPage">
|
|
||||||
<mk-user-name :user="user"/>
|
|
||||||
</router-link></li>
|
|
||||||
</ul>
|
|
||||||
</main>
|
</main>
|
||||||
</mk-ui>
|
</mk-ui>
|
||||||
</template>
|
</template>
|
||||||
|
@ -15,13 +11,16 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import Progress from '../../../common/scripts/loading';
|
import Progress from '../../../common/scripts/loading';
|
||||||
|
import XEditor from '../../../common/views/components/user-list-editor.vue';
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
|
components: {
|
||||||
|
XEditor
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
fetching: true,
|
fetching: true,
|
||||||
list: null,
|
list: null
|
||||||
users: null
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
|
@ -42,12 +41,6 @@ export default Vue.extend({
|
||||||
this.fetching = false;
|
this.fetching = false;
|
||||||
|
|
||||||
Progress.done();
|
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>
|
</script>
|
||||||
|
|
||||||
<style lang="stylus" scoped>
|
<style lang="stylus" scoped>
|
||||||
|
|
||||||
|
|
||||||
main
|
main
|
||||||
width 100%
|
width 100%
|
||||||
max-width 680px
|
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