diff options
| author | syuilo <syuilotan@yahoo.co.jp> | 2018-12-19 07:22:01 +0900 |
|---|---|---|
| committer | syuilo <syuilotan@yahoo.co.jp> | 2018-12-19 07:22:01 +0900 |
| commit | e88ce1746ddab75baa2fbb70efa594c0b126859d (patch) | |
| tree | 01ee7aa0e1caca248f79d824e5a9a7ac464e26c7 /src | |
| parent | Fix error (diff) | |
| download | misskey-e88ce1746ddab75baa2fbb70efa594c0b126859d.tar.gz misskey-e88ce1746ddab75baa2fbb70efa594c0b126859d.tar.bz2 misskey-e88ce1746ddab75baa2fbb70efa594c0b126859d.zip | |
リスト関連の操作を強化
Resolve #2069
Resolve #2051
Resolve #2807
Resolve #3647
Diffstat (limited to 'src')
6 files changed, 249 insertions, 17 deletions
diff --git a/src/client/app/common/views/components/user-list-editor.vue b/src/client/app/common/views/components/user-list-editor.vue new file mode 100644 index 0000000000..1b068da86a --- /dev/null +++ b/src/client/app/common/views/components/user-list-editor.vue @@ -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> diff --git a/src/client/app/desktop/views/components/ui.header.account.vue b/src/client/app/desktop/views/components/ui.header.account.vue index 09f407b0ed..bba106616c 100644 --- a/src/client/app/desktop/views/components/ui.header.account.vue +++ b/src/client/app/desktop/views/components/ui.header.account.vue @@ -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() { diff --git a/src/client/app/desktop/views/components/user-list-window.vue b/src/client/app/desktop/views/components/user-list-window.vue new file mode 100644 index 0000000000..054a133a4c --- /dev/null +++ b/src/client/app/desktop/views/components/user-list-window.vue @@ -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> diff --git a/src/client/app/desktop/views/components/user-lists-window.vue b/src/client/app/desktop/views/components/user-lists-window.vue index 89a0d7b9e3..4ecbc760e5 100644 --- a/src/client/app/desktop/views/components/user-lists-window.vue +++ b/src/client/app/desktop/views/components/user-lists-window.vue @@ -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"> diff --git a/src/client/app/mobile/views/pages/user-list.vue b/src/client/app/mobile/views/pages/user-list.vue index 32e332838b..cf2dd134fd 100644 --- a/src/client/app/mobile/views/pages/user-list.vue +++ b/src/client/app/mobile/views/pages/user-list.vue @@ -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 diff --git a/src/server/api/endpoints/users/lists/pull.ts b/src/server/api/endpoints/users/lists/pull.ts new file mode 100644 index 0000000000..6b755ca88f --- /dev/null +++ b/src/server/api/endpoints/users/lists/pull.ts @@ -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)); +})); |