aboutsummaryrefslogtreecommitdiff
path: root/src/components/lists_edit
diff options
context:
space:
mode:
Diffstat (limited to 'src/components/lists_edit')
-rw-r--r--src/components/lists_edit/lists_edit.js145
-rw-r--r--src/components/lists_edit/lists_edit.vue228
2 files changed, 373 insertions, 0 deletions
diff --git a/src/components/lists_edit/lists_edit.js b/src/components/lists_edit/lists_edit.js
new file mode 100644
index 00000000..c22d1323
--- /dev/null
+++ b/src/components/lists_edit/lists_edit.js
@@ -0,0 +1,145 @@
+import { mapState, mapGetters } from 'vuex'
+import BasicUserCard from '../basic_user_card/basic_user_card.vue'
+import ListsUserSearch from '../lists_user_search/lists_user_search.vue'
+import PanelLoading from 'src/components/panel_loading/panel_loading.vue'
+import UserAvatar from '../user_avatar/user_avatar.vue'
+import TabSwitcher from 'src/components/tab_switcher/tab_switcher.jsx'
+import { library } from '@fortawesome/fontawesome-svg-core'
+import {
+ faSearch,
+ faChevronLeft
+} from '@fortawesome/free-solid-svg-icons'
+
+library.add(
+ faSearch,
+ faChevronLeft
+)
+
+const ListsNew = {
+ components: {
+ BasicUserCard,
+ UserAvatar,
+ ListsUserSearch,
+ TabSwitcher,
+ PanelLoading
+ },
+ data () {
+ return {
+ title: '',
+ titleDraft: '',
+ membersUserIds: [],
+ removedUserIds: new Set([]), // users we added for members, to undo
+ searchUserIds: [],
+ addedUserIds: new Set([]), // users we added from search, to undo
+ searchLoading: false,
+ reallyDelete: false
+ }
+ },
+ created () {
+ if (!this.id) return
+ this.$store.dispatch('fetchList', { listId: this.id })
+ .then(() => {
+ this.title = this.findListTitle(this.id)
+ this.titleDraft = this.title
+ })
+ this.$store.dispatch('fetchListAccounts', { listId: this.id })
+ .then(() => {
+ this.membersUserIds = this.findListAccounts(this.id)
+ this.membersUserIds.forEach(userId => {
+ this.$store.dispatch('fetchUserIfMissing', userId)
+ })
+ })
+ },
+ computed: {
+ id () {
+ return this.$route.params.id
+ },
+ membersUsers () {
+ return [...this.membersUserIds, ...this.addedUserIds]
+ .map(userId => this.findUser(userId)).filter(user => user)
+ },
+ searchUsers () {
+ return this.searchUserIds.map(userId => this.findUser(userId)).filter(user => user)
+ },
+ ...mapState({
+ currentUser: state => state.users.currentUser
+ }),
+ ...mapGetters(['findUser', 'findListTitle', 'findListAccounts'])
+ },
+ methods: {
+ onInput () {
+ this.search(this.query)
+ },
+ toggleRemoveMember (user) {
+ if (this.removedUserIds.has(user.id)) {
+ this.id && this.addUser(user)
+ this.removedUserIds.delete(user.id)
+ } else {
+ this.id && this.removeUser(user.id)
+ this.removedUserIds.add(user.id)
+ }
+ },
+ toggleAddFromSearch (user) {
+ if (this.addedUserIds.has(user.id)) {
+ this.id && this.removeUser(user.id)
+ this.addedUserIds.delete(user.id)
+ } else {
+ this.id && this.addUser(user)
+ this.addedUserIds.add(user.id)
+ }
+ },
+ isRemoved (user) {
+ return this.removedUserIds.has(user.id)
+ },
+ isAdded (user) {
+ return this.addedUserIds.has(user.id)
+ },
+ addUser (user) {
+ this.$store.dispatch('addListAccount', { accountId: this.user.id, listId: this.id })
+ },
+ removeUser (userId) {
+ this.$store.dispatch('removeListAccount', { accountId: this.user.id, listId: this.id })
+ },
+ onSearchLoading (results) {
+ this.searchLoading = true
+ },
+ onSearchLoadingDone (results) {
+ this.searchLoading = false
+ },
+ onSearchResults (results) {
+ this.searchLoading = false
+ this.searchUserIds = results
+ },
+ updateListTitle () {
+ this.$store.dispatch('setList', { listId: this.id, title: this.titleDraft })
+ .then(() => {
+ this.title = this.findListTitle(this.id)
+ })
+ },
+ createList () {
+ this.$store.dispatch('createList', { title: this.titleDraft })
+ .then((list) => {
+ return this
+ .$store
+ .dispatch('setListAccounts', { listId: list.id, accountIds: [...this.addedUserIds] })
+ .then(() => list.id)
+ })
+ .then((listId) => {
+ this.$router.push({ name: 'lists-timeline', params: { id: listId } })
+ })
+ .catch((e) => {
+ this.$store.dispatch('pushGlobalNotice', {
+ messageKey: 'lists.error',
+ messageArgs: [e.message],
+ level: 'error'
+ })
+ })
+ },
+ deleteList () {
+ this.$store.dispatch('deleteList', { listId: this.id })
+ this.$router.push({ name: 'lists' })
+ }
+ }
+}
+
+export default ListsNew
diff --git a/src/components/lists_edit/lists_edit.vue b/src/components/lists_edit/lists_edit.vue
new file mode 100644
index 00000000..6521aba6
--- /dev/null
+++ b/src/components/lists_edit/lists_edit.vue
@@ -0,0 +1,228 @@
+<template>
+ <div class="panel-default panel ListEdit">
+ <div
+ ref="header"
+ class="panel-heading list-edit-heading"
+ >
+ <button
+ class="button-unstyled go-back-button"
+ @click="$router.back"
+ >
+ <FAIcon
+ size="lg"
+ icon="chevron-left"
+ />
+ </button>
+ <div class="title">
+ <i18n-t
+ v-if="id"
+ keypath="lists.editing_list"
+ >
+ <template #listTitle>
+ {{ title }}
+ </template>
+ </i18n-t>
+ <i18n-t
+ v-else
+ keypath="lists.creating_list"
+ />
+ </div>
+ </div>
+ <div class="panel-body">
+ <div class="input-wrap">
+ <label for="list-edit-title">{{ $t('lists.title') }}</label>
+ {{ ' ' }}
+ <input
+ id="list-edit-title"
+ ref="title"
+ v-model="titleDraft"
+ >
+ <button
+ v-if="id"
+ class="btn button-default follow-button"
+ @click="updateListTitle"
+ >
+ {{ $t('lists.update_title') }}
+ </button>
+ </div>
+ <tab-switcher
+ class="list-member-management"
+ :scrollable-tabs="true"
+ >
+ <div
+ v-if="id || addedUserIds.size > 0"
+ :label="$t('lists.manage_members')"
+ class="members-list"
+ >
+ <div class="users-list">
+ <div
+ v-for="user in membersUsers"
+ :key="user.id"
+ class="member"
+ >
+ <BasicUserCard
+ :user="user"
+ >
+ <button
+ class="btn button-default follow-button"
+ @click="toggleRemoveMember(user)"
+ >
+ {{ isRemoved(user) ? $t('general.undo') : $t('lists.remove_from_list') }}
+ </button>
+ </BasicUserCard>
+ </div>
+ </div>
+ </div>
+
+ <div
+ class="search-list"
+ :label="$t('lists.add_members')"
+ >
+ <ListsUserSearch
+ @results="onSearchResults"
+ @loading="onSearchLoading"
+ @loadingDone="onSearchLoadingDone"
+ />
+ <div
+ v-if="searchLoading"
+ class="loading"
+ >
+ <PanelLoading />
+ </div>
+ <div
+ v-else
+ class="users-list"
+ >
+ <div
+ v-for="user in searchUsers"
+ :key="user.id"
+ class="member"
+ >
+ <BasicUserCard
+ :user="user"
+ >
+ <span
+ v-if="membersUserIds.includes(user.id)"
+ >
+ {{ $t('lists.is_in_list') }}
+ </span>
+ <button
+ v-if="!membersUserIds.includes(user.id)"
+ class="btn button-default follow-button"
+ @click="toggleAddFromSearch(user)"
+ >
+ {{ isAdded(user) ? $t('general.undo') : $t('lists.add_to_list') }}
+ </button>
+ <button
+ v-else
+ class="btn button-default follow-button"
+ @click="toggleRemoveMember(user)"
+ >
+ {{ isRemoved(user) ? $t('general.undo') : $t('lists.remove_from_list') }}
+ </button>
+ </BasicUserCard>
+ </div>
+ </div>
+ </div>
+ </tab-switcher>
+ </div>
+ <div class="panel-footer">
+ <span class="spacer" />
+ <button
+ v-if="!id"
+ class="btn button-default footer-button"
+ @click="createList"
+ >
+ {{ $t('lists.create') }}
+ </button>
+ <button
+ v-else-if="!reallyDelete"
+ class="btn button-default footer-button"
+ @click="reallyDelete = true"
+ >
+ {{ $t('lists.delete') }}
+ </button>
+ <template v-else>
+ {{ $t('lists.really_delete') }}
+ <button
+ class="btn button-default footer-button"
+ @click="deleteList"
+ >
+ {{ $t('general.yes') }}
+ </button>
+ <button
+ class="btn button-default footer-button"
+ @click="reallyDelete = false"
+ >
+ {{ $t('general.no') }}
+ </button>
+ </template>
+ </div>
+ </div>
+</template>
+
+<script src="./lists_edit.js"></script>
+
+<style lang="scss">
+@import '../../_variables.scss';
+
+.ListEdit {
+ --panel-body-padding: 0.5em;
+
+ height: calc(100vh - var(--navbar-height));
+ overflow: hidden;
+ display: flex;
+ flex-direction: column;
+
+ .list-edit-heading {
+ grid-template-columns: auto minmax(50%, 1fr);
+ }
+
+ .panel-body {
+ display: flex;
+ flex: 1;
+ flex-direction: column;
+ overflow: hidden;
+ }
+
+ .list-member-management {
+ flex: 1 0 auto;
+ }
+
+ .search-icon {
+ margin-right: 0.3em;
+ }
+
+ .users-list {
+ padding-bottom: 0.7rem;
+ overflow-y: auto;
+ }
+
+ & .search-list,
+ & .members-list {
+ overflow: hidden;
+ flex-direction: column;
+ min-height: 0;
+ }
+
+ .go-back-button {
+ text-align: center;
+ line-height: 1;
+ height: 100%;
+ align-self: start;
+ width: var(--__panel-heading-height-inner);
+ }
+
+ .btn {
+ margin: 0 0.5em;
+ }
+
+ .panel-footer {
+ grid-template-columns: minmax(10%, 1fr);
+
+ .footer-button {
+ min-width: 9em;
+ }
+ }
+}
+</style>