summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorsyuilo <syuilotan@yahoo.co.jp>2018-12-19 07:22:01 +0900
committersyuilo <syuilotan@yahoo.co.jp>2018-12-19 07:22:01 +0900
commite88ce1746ddab75baa2fbb70efa594c0b126859d (patch)
tree01ee7aa0e1caca248f79d824e5a9a7ac464e26c7 /src
parentFix error (diff)
downloadmisskey-e88ce1746ddab75baa2fbb70efa594c0b126859d.tar.gz
misskey-e88ce1746ddab75baa2fbb70efa594c0b126859d.tar.bz2
misskey-e88ce1746ddab75baa2fbb70efa594c0b126859d.zip
リスト関連の操作を強化
Resolve #2069 Resolve #2051 Resolve #2807 Resolve #3647
Diffstat (limited to 'src')
-rw-r--r--src/client/app/common/views/components/user-list-editor.vue150
-rw-r--r--src/client/app/desktop/views/components/ui.header.account.vue5
-rw-r--r--src/client/app/desktop/views/components/user-list-window.vue24
-rw-r--r--src/client/app/desktop/views/components/user-lists-window.vue2
-rw-r--r--src/client/app/mobile/views/pages/user-list.vue21
-rw-r--r--src/server/api/endpoints/users/lists/pull.ts64
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));
+}));