From d5d464a28992a634f380f6a50a1a04c88f0a4dfe Mon Sep 17 00:00:00 2001 From: Sol Fisher Romanoff Date: Wed, 15 Jun 2022 17:12:05 +0300 Subject: [PATCH 01/17] Add list timeline rendering functionality the list name is missing; this is because the API request to get it is different to the API request for receiving the list timeline, and is not yet implemented. --- src/boot/routes.js | 4 +++- src/components/list_timeline/list_timeline.js | 23 +++++++++++++++++++ .../list_timeline/list_timeline.vue | 10 ++++++++ src/components/timeline/timeline.js | 3 +++ src/modules/api.js | 5 ++-- src/modules/statuses.js | 3 ++- src/services/api/api.service.js | 7 ++++++ .../backend_interactor_service.js | 4 ++-- .../timeline_fetcher.service.js | 14 +++++++---- 9 files changed, 62 insertions(+), 11 deletions(-) create mode 100644 src/components/list_timeline/list_timeline.js create mode 100644 src/components/list_timeline/list_timeline.vue diff --git a/src/boot/routes.js b/src/boot/routes.js index 726476a8..6eab04a4 100644 --- a/src/boot/routes.js +++ b/src/boot/routes.js @@ -20,6 +20,7 @@ import ShoutPanel from 'components/shout_panel/shout_panel.vue' import WhoToFollow from 'components/who_to_follow/who_to_follow.vue' import About from 'components/about/about.vue' import RemoteUserResolver from 'components/remote_user_resolver/remote_user_resolver.vue' +import ListTimeline from 'components/list_timeline/list_timeline.vue' export default (store) => { const validateAuthenticatedRoute = (to, from, next) => { @@ -69,7 +70,8 @@ export default (store) => { { name: 'search', path: '/search', component: Search, props: (route) => ({ query: route.query.query }) }, { name: 'who-to-follow', path: '/who-to-follow', component: WhoToFollow, beforeEnter: validateAuthenticatedRoute }, { name: 'about', path: '/about', component: About }, - { name: 'user-profile', path: '/:_(users)?/:name', component: UserProfile } + { name: 'user-profile', path: '/:_(users)?/:name', component: UserProfile }, + { name: 'list-timeline', path: '/lists/:id', component: ListTimeline } ] if (store.state.instance.pleromaChatMessagesAvailable) { diff --git a/src/components/list_timeline/list_timeline.js b/src/components/list_timeline/list_timeline.js new file mode 100644 index 00000000..044dbd35 --- /dev/null +++ b/src/components/list_timeline/list_timeline.js @@ -0,0 +1,23 @@ +import Timeline from '../timeline/timeline.vue' +const ListTimeline = { + data () { + return { + listId: null + } + }, + components: { + Timeline + }, + computed: { + timeline () { return this.$store.state.statuses.timelines.list } + }, + created () { + this.listId = this.$route.params.id + this.$store.dispatch('startFetchingTimeline', { timeline: 'list', listId: this.listId }) + }, + unmounted () { + this.$store.dispatch('stopFetchingTimeline', 'list') + } +} + +export default ListTimeline diff --git a/src/components/list_timeline/list_timeline.vue b/src/components/list_timeline/list_timeline.vue new file mode 100644 index 00000000..a368f861 --- /dev/null +++ b/src/components/list_timeline/list_timeline.vue @@ -0,0 +1,10 @@ + + + diff --git a/src/components/timeline/timeline.js b/src/components/timeline/timeline.js index c575e876..5b6c5089 100644 --- a/src/components/timeline/timeline.js +++ b/src/components/timeline/timeline.js @@ -18,6 +18,7 @@ const Timeline = { 'timelineName', 'title', 'userId', + 'listId', 'tag', 'embedded', 'count', @@ -101,6 +102,7 @@ const Timeline = { timeline: this.timelineName, showImmediately, userId: this.userId, + listId: this.listId, tag: this.tag }) }, @@ -156,6 +158,7 @@ const Timeline = { older: true, showImmediately: true, userId: this.userId, + listId: this.listId, tag: this.tag }).then(({ statuses }) => { if (statuses && statuses.length === 0) { diff --git a/src/modules/api.js b/src/modules/api.js index 54f94356..95ed99d2 100644 --- a/src/modules/api.js +++ b/src/modules/api.js @@ -191,12 +191,13 @@ const api = { startFetchingTimeline (store, { timeline = 'friends', tag = false, - userId = false + userId = false, + listId = false }) { if (store.state.fetchers[timeline]) return const fetcher = store.state.backendInteractor.startFetchingTimeline({ - timeline, store, userId, tag + timeline, store, userId, listId, tag }) store.commit('addFetcher', { fetcherName: timeline, fetcher }) }, diff --git a/src/modules/statuses.js b/src/modules/statuses.js index a13930e9..ea48f2d6 100644 --- a/src/modules/statuses.js +++ b/src/modules/statuses.js @@ -62,7 +62,8 @@ export const defaultState = () => ({ friends: emptyTl(), tag: emptyTl(), dms: emptyTl(), - bookmarks: emptyTl() + bookmarks: emptyTl(), + list: emptyTl() } }) diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js index e7a64337..b51ec6be 100644 --- a/src/services/api/api.service.js +++ b/src/services/api/api.service.js @@ -50,6 +50,7 @@ const MASTODON_STATUS_CONTEXT_URL = id => `/api/v1/statuses/${id}/context` const MASTODON_USER_URL = '/api/v1/accounts' const MASTODON_USER_RELATIONSHIPS_URL = '/api/v1/accounts/relationships' const MASTODON_USER_TIMELINE_URL = id => `/api/v1/accounts/${id}/statuses` +const MASTODON_LIST_TIMELINE_URL = id => `/api/v1/timelines/list/${id}` const MASTODON_TAG_TIMELINE_URL = tag => `/api/v1/timelines/tag/${tag}` const MASTODON_BOOKMARK_TIMELINE_URL = '/api/v1/bookmarks' const MASTODON_USER_BLOCKS_URL = '/api/v1/blocks/' @@ -503,6 +504,7 @@ const fetchTimeline = ({ since = false, until = false, userId = false, + listId = false, tag = false, withMuted = false, replyVisibility = 'all' @@ -515,6 +517,7 @@ const fetchTimeline = ({ 'publicAndExternal': MASTODON_PUBLIC_TIMELINE, user: MASTODON_USER_TIMELINE_URL, media: MASTODON_USER_TIMELINE_URL, + list: MASTODON_LIST_TIMELINE_URL, favorites: MASTODON_USER_FAVORITES_TIMELINE_URL, tag: MASTODON_TAG_TIMELINE_URL, bookmarks: MASTODON_BOOKMARK_TIMELINE_URL @@ -528,6 +531,10 @@ const fetchTimeline = ({ url = url(userId) } + if (timeline === 'list') { + url = url(listId) + } + if (since) { params.push(['since_id', since]) } diff --git a/src/services/backend_interactor_service/backend_interactor_service.js b/src/services/backend_interactor_service/backend_interactor_service.js index 4a40f5b5..71740ba4 100644 --- a/src/services/backend_interactor_service/backend_interactor_service.js +++ b/src/services/backend_interactor_service/backend_interactor_service.js @@ -4,8 +4,8 @@ import notificationsFetcher from '../notifications_fetcher/notifications_fetcher import followRequestFetcher from '../../services/follow_request_fetcher/follow_request_fetcher.service' const backendInteractorService = credentials => ({ - startFetchingTimeline ({ timeline, store, userId = false, tag }) { - return timelineFetcher.startFetching({ timeline, store, credentials, userId, tag }) + startFetchingTimeline ({ timeline, store, userId = false, listId = false, tag }) { + return timelineFetcher.startFetching({ timeline, store, credentials, userId, listId, tag }) }, fetchTimeline (args) { diff --git a/src/services/timeline_fetcher/timeline_fetcher.service.js b/src/services/timeline_fetcher/timeline_fetcher.service.js index 3ada329b..49d7cdc8 100644 --- a/src/services/timeline_fetcher/timeline_fetcher.service.js +++ b/src/services/timeline_fetcher/timeline_fetcher.service.js @@ -3,12 +3,13 @@ import { camelCase } from 'lodash' import apiService from '../api/api.service.js' import { promiseInterval } from '../promise_interval/promise_interval.js' -const update = ({ store, statuses, timeline, showImmediately, userId, pagination }) => { +const update = ({ store, statuses, timeline, showImmediately, userId, listId, pagination }) => { const ccTimeline = camelCase(timeline) store.dispatch('addNewStatuses', { timeline: ccTimeline, userId, + listId, statuses, showImmediately, pagination @@ -22,6 +23,7 @@ const fetchAndUpdate = ({ older = false, showImmediately = false, userId = false, + listId = false, tag = false, until, since @@ -44,6 +46,7 @@ const fetchAndUpdate = ({ } args['userId'] = userId + args['listId'] = listId args['tag'] = tag args['withMuted'] = !hideMutedPosts if (loggedIn && ['friends', 'public', 'publicAndExternal'].includes(timeline)) { @@ -62,7 +65,7 @@ const fetchAndUpdate = ({ if (!older && statuses.length >= 20 && !timelineData.loading && numStatusesBeforeFetch > 0) { store.dispatch('queueFlush', { timeline: timeline, id: timelineData.maxId }) } - update({ store, statuses, timeline, showImmediately, userId, pagination }) + update({ store, statuses, timeline, showImmediately, userId, listId, pagination }) return { statuses, pagination } }) .catch((error) => { @@ -75,14 +78,15 @@ const fetchAndUpdate = ({ }) } -const startFetching = ({ timeline = 'friends', credentials, store, userId = false, tag = false }) => { +const startFetching = ({ timeline = 'friends', credentials, store, userId = false, listId = false, tag = false }) => { const rootState = store.rootState || store.state const timelineData = rootState.statuses.timelines[camelCase(timeline)] const showImmediately = timelineData.visibleStatuses.length === 0 timelineData.userId = userId - fetchAndUpdate({ timeline, credentials, store, showImmediately, userId, tag }) + timelineData.listId = listId + fetchAndUpdate({ timeline, credentials, store, showImmediately, userId, listId, tag }) const boundFetchAndUpdate = () => - fetchAndUpdate({ timeline, credentials, store, userId, tag }) + fetchAndUpdate({ timeline, credentials, store, userId, listId, tag }) return promiseInterval(boundFetchAndUpdate, 20000) } const timelineFetcher = { From e18e179a59c70260478106fba0694c3a4fc1d918 Mon Sep 17 00:00:00 2001 From: Sol Fisher Romanoff Date: Wed, 15 Jun 2022 18:34:11 +0300 Subject: [PATCH 02/17] Fetch list of lists from the API --- src/modules/api.js | 18 ++++++++++++++- src/services/api/api.service.js | 8 +++++++ .../backend_interactor_service.js | 5 +++++ .../lists_fetcher/lists_fetcher.service.js | 22 +++++++++++++++++++ 4 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 src/services/lists_fetcher/lists_fetcher.service.js diff --git a/src/modules/api.js b/src/modules/api.js index 95ed99d2..d9ae21f8 100644 --- a/src/modules/api.js +++ b/src/modules/api.js @@ -13,7 +13,8 @@ const api = { socket: null, mastoUserSocket: null, mastoUserSocketStatus: null, - followRequests: [] + followRequests: [], + lists: [] }, mutations: { setBackendInteractor (state, backendInteractor) { @@ -35,6 +36,9 @@ const api = { setFollowRequests (state, value) { state.followRequests = value }, + setLists (state, value) { + state.lists = value + }, setMastoUserSocketStatus (state, value) { state.mastoUserSocketStatus = value }, @@ -249,6 +253,18 @@ const api = { store.commit('setFollowRequests', requests) }, + // Lists + startFetchingLists (store) { + if (store.state.fetchers['lists']) return + const fetcher = store.state.backendInteractor.startFetchingLists({ store }) + store.commit('addFetcher', { fetcherName: 'lists', fetcher }) + }, + stopFetchingLists (store) { + const fetcher = store.state.fetchers.lists + if (!fetcher) return + store.commit('removeFetcher', { fetcherName: 'lists', fetcher }) + }, + // Pleroma websocket setWsToken (store, token) { store.commit('setWsToken', token) diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js index b51ec6be..7c7e657d 100644 --- a/src/services/api/api.service.js +++ b/src/services/api/api.service.js @@ -79,6 +79,7 @@ const MASTODON_SEARCH_2 = `/api/v2/search` const MASTODON_USER_SEARCH_URL = '/api/v1/accounts/search' const MASTODON_MASCOT_URL = '/api/v1/pleroma/mascot' const MASTODON_DOMAIN_BLOCKS_URL = '/api/v1/domain_blocks' +const MASTODON_LISTS_URL = '/api/v1/lists' const MASTODON_STREAMING = '/api/v1/streaming' const MASTODON_KNOWN_DOMAIN_LIST_URL = '/api/v1/instance/peers' const PLEROMA_EMOJI_REACTIONS_URL = id => `/api/v1/pleroma/statuses/${id}/reactions` @@ -383,6 +384,12 @@ const fetchFollowRequests = ({ credentials }) => { .then((data) => data.map(parseUser)) } +const fetchLists = ({ credentials }) => { + const url = MASTODON_LISTS_URL + return fetch(url, { headers: authHeaders(credentials) }) + .then((data) => data.json()) +} + const fetchConversation = ({ id, credentials }) => { let urlContext = MASTODON_STATUS_CONTEXT_URL(id) return fetch(urlContext, { headers: authHeaders(credentials) }) @@ -1355,6 +1362,7 @@ const apiService = { mfaSetupOTP, mfaConfirmOTP, fetchFollowRequests, + fetchLists, approveUser, denyUser, suggestions, diff --git a/src/services/backend_interactor_service/backend_interactor_service.js b/src/services/backend_interactor_service/backend_interactor_service.js index 71740ba4..62ee8549 100644 --- a/src/services/backend_interactor_service/backend_interactor_service.js +++ b/src/services/backend_interactor_service/backend_interactor_service.js @@ -2,6 +2,7 @@ import apiService, { getMastodonSocketURI, ProcessedWS } from '../api/api.servic import timelineFetcher from '../timeline_fetcher/timeline_fetcher.service.js' import notificationsFetcher from '../notifications_fetcher/notifications_fetcher.service.js' import followRequestFetcher from '../../services/follow_request_fetcher/follow_request_fetcher.service' +import listsFetcher from '../../services/lists_fetcher/lists_fetcher.service.js' const backendInteractorService = credentials => ({ startFetchingTimeline ({ timeline, store, userId = false, listId = false, tag }) { @@ -24,6 +25,10 @@ const backendInteractorService = credentials => ({ return followRequestFetcher.startFetching({ store, credentials }) }, + startFetchingLists ({ store }) { + return listsFetcher.startFetching({ store, credentials }) + }, + startUserSocket ({ store }) { const serv = store.rootState.instance.server.replace('http', 'ws') const url = serv + getMastodonSocketURI({ credentials, stream: 'user' }) diff --git a/src/services/lists_fetcher/lists_fetcher.service.js b/src/services/lists_fetcher/lists_fetcher.service.js new file mode 100644 index 00000000..8d9dae66 --- /dev/null +++ b/src/services/lists_fetcher/lists_fetcher.service.js @@ -0,0 +1,22 @@ +import apiService from '../api/api.service.js' +import { promiseInterval } from '../promise_interval/promise_interval.js' + +const fetchAndUpdate = ({ store, credentials }) => { + return apiService.fetchLists({ credentials }) + .then(lists => { + store.commit('setLists', lists) + }, () => {}) + .catch(() => {}) +} + +const startFetching = ({ credentials, store }) => { + const boundFetchAndUpdate = () => fetchAndUpdate({ credentials, store }) + boundFetchAndUpdate() + return promiseInterval(boundFetchAndUpdate, 240000) +} + +const listsFetcher = { + startFetching +} + +export default listsFetcher From 6e8c7460a2dca37eb2264c3b3788d1baf6517108 Mon Sep 17 00:00:00 2001 From: Sol Fisher Romanoff Date: Wed, 15 Jun 2022 19:13:33 +0300 Subject: [PATCH 03/17] Add lists listing page --- src/boot/routes.js | 2 ++ src/components/list_card/list_card.js | 12 ++++++++++++ src/components/list_card/list_card.vue | 9 +++++++++ src/components/lists/lists.js | 17 +++++++++++++++++ src/components/lists/lists.vue | 18 ++++++++++++++++++ src/i18n/en.json | 3 +++ 6 files changed, 61 insertions(+) create mode 100644 src/components/list_card/list_card.js create mode 100644 src/components/list_card/list_card.vue create mode 100644 src/components/lists/lists.js create mode 100644 src/components/lists/lists.vue diff --git a/src/boot/routes.js b/src/boot/routes.js index 6eab04a4..715b394e 100644 --- a/src/boot/routes.js +++ b/src/boot/routes.js @@ -20,6 +20,7 @@ import ShoutPanel from 'components/shout_panel/shout_panel.vue' import WhoToFollow from 'components/who_to_follow/who_to_follow.vue' import About from 'components/about/about.vue' import RemoteUserResolver from 'components/remote_user_resolver/remote_user_resolver.vue' +import Lists from 'components/lists/lists.vue' import ListTimeline from 'components/list_timeline/list_timeline.vue' export default (store) => { @@ -71,6 +72,7 @@ export default (store) => { { name: 'who-to-follow', path: '/who-to-follow', component: WhoToFollow, beforeEnter: validateAuthenticatedRoute }, { name: 'about', path: '/about', component: About }, { name: 'user-profile', path: '/:_(users)?/:name', component: UserProfile }, + { name: 'lists', path: '/lists', component: Lists }, { name: 'list-timeline', path: '/lists/:id', component: ListTimeline } ] diff --git a/src/components/list_card/list_card.js b/src/components/list_card/list_card.js new file mode 100644 index 00000000..42e56aff --- /dev/null +++ b/src/components/list_card/list_card.js @@ -0,0 +1,12 @@ +const ListCard = { + props: [ + 'list' + ], + methods: { + listLink (id) { + return '/lists/' + id + } + } +} + +export default ListCard diff --git a/src/components/list_card/list_card.vue b/src/components/list_card/list_card.vue new file mode 100644 index 00000000..7d6363ba --- /dev/null +++ b/src/components/list_card/list_card.vue @@ -0,0 +1,9 @@ + + + diff --git a/src/components/lists/lists.js b/src/components/lists/lists.js new file mode 100644 index 00000000..bb67fafb --- /dev/null +++ b/src/components/lists/lists.js @@ -0,0 +1,17 @@ +import ListCard from '../list_card/list_card.vue' + +const Lists = { + components: { + ListCard + }, + created () { + this.$store.dispatch('startFetchingLists') + }, + computed: { + lists () { + return this.$store.state.api.lists + } + } +} + +export default Lists diff --git a/src/components/lists/lists.vue b/src/components/lists/lists.vue new file mode 100644 index 00000000..da0f126f --- /dev/null +++ b/src/components/lists/lists.vue @@ -0,0 +1,18 @@ + + + diff --git a/src/i18n/en.json b/src/i18n/en.json index ec2882c5..65198863 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -946,6 +946,9 @@ "error_sending_message": "Something went wrong when sending the message.", "empty_chat_list_placeholder": "You don't have any chats yet. Start a new chat!" }, + "lists": { + "lists": "Lists" + }, "file_type": { "audio": "Audio", "video": "Video", From d2c82a04d9814a040737f839cfdf0038f7a80b60 Mon Sep 17 00:00:00 2001 From: Sol Fisher Romanoff Date: Tue, 14 Jun 2022 23:53:51 +0300 Subject: [PATCH 04/17] Add buttons to menus --- src/components/nav_panel/nav_panel.js | 6 ++++-- src/components/nav_panel/nav_panel.vue | 12 ++++++++++++ src/components/side_drawer/side_drawer.js | 6 ++++-- src/components/side_drawer/side_drawer.vue | 12 ++++++++++++ src/i18n/en.json | 3 ++- 5 files changed, 34 insertions(+), 5 deletions(-) diff --git a/src/components/nav_panel/nav_panel.js b/src/components/nav_panel/nav_panel.js index 37bcb409..f52fc677 100644 --- a/src/components/nav_panel/nav_panel.js +++ b/src/components/nav_panel/nav_panel.js @@ -12,7 +12,8 @@ import { faComments, faBell, faInfoCircle, - faStream + faStream, + faList } from '@fortawesome/free-solid-svg-icons' library.add( @@ -25,7 +26,8 @@ library.add( faComments, faBell, faInfoCircle, - faStream + faStream, + faList ) const NavPanel = { diff --git a/src/components/nav_panel/nav_panel.vue b/src/components/nav_panel/nav_panel.vue index 7ae7b1d6..c139549d 100644 --- a/src/components/nav_panel/nav_panel.vue +++ b/src/components/nav_panel/nav_panel.vue @@ -25,6 +25,18 @@ +
  • + + {{ $t("nav.lists") }} + +
  • {{ $t("nav.timelines") }}
  • +
  • + + {{ $t("nav.lists") }} + +
  • Date: Wed, 15 Jun 2022 20:32:46 +0300 Subject: [PATCH 05/17] Make the list list page look good --- src/components/list_card/list_card.vue | 34 ++++++++++++++++++++++---- src/components/lists/lists.vue | 1 + 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/src/components/list_card/list_card.vue b/src/components/list_card/list_card.vue index 7d6363ba..5b4fefc8 100644 --- a/src/components/list_card/list_card.vue +++ b/src/components/list_card/list_card.vue @@ -1,9 +1,33 @@ + + diff --git a/src/components/lists/lists.vue b/src/components/lists/lists.vue index da0f126f..20dd46d8 100644 --- a/src/components/lists/lists.vue +++ b/src/components/lists/lists.vue @@ -10,6 +10,7 @@ v-for="list in lists.slice().reverse()" :key="list" :list="list" + class="list-item" /> From bacb6c8fb38310a44e583e87b621cec4b61a7a79 Mon Sep 17 00:00:00 2001 From: Sol Fisher Romanoff Date: Thu, 16 Jun 2022 01:52:24 +0300 Subject: [PATCH 06/17] Add list creation functionality --- src/components/list_new/list_new.js | 97 ++++++++++++++++++++++++++ src/components/list_new/list_new.vue | 100 +++++++++++++++++++++++++++ src/components/lists/lists.js | 17 ++++- src/components/lists/lists.vue | 14 +++- src/i18n/en.json | 6 +- src/services/api/api.service.js | 27 ++++++++ 6 files changed, 258 insertions(+), 3 deletions(-) create mode 100644 src/components/list_new/list_new.js create mode 100644 src/components/list_new/list_new.vue diff --git a/src/components/list_new/list_new.js b/src/components/list_new/list_new.js new file mode 100644 index 00000000..7ac7e0f0 --- /dev/null +++ b/src/components/list_new/list_new.js @@ -0,0 +1,97 @@ +import { mapState, mapGetters } from 'vuex' +import BasicUserCard from '../basic_user_card/basic_user_card.vue' +import UserAvatar from '../user_avatar/user_avatar.vue' +import { library } from '@fortawesome/fontawesome-svg-core' +import { + faSearch, + faChevronLeft +} from '@fortawesome/free-solid-svg-icons' + +library.add( + faSearch, + faChevronLeft +) + +const ListNew = { + components: { + BasicUserCard, + UserAvatar + }, + data () { + return { + title: '', + suggestions: [], + userIds: [], + selectedUserIds: [], + loading: false, + query: '' + } + }, + computed: { + users () { + return this.userIds.map(userId => this.findUser(userId)) + }, + availableUsers () { + if (this.query.length !== 0) { + return this.users + } else { + return this.suggestions + } + }, + ...mapState({ + currentUser: state => state.users.currentUser, + backendInteractor: state => state.api.backendInteractor + }), + ...mapGetters(['findUser']) + }, + methods: { + goBack () { + this.$emit('cancel') + }, + onInput () { + this.search(this.query) + }, + selectUser (user, event) { + if (this.selectedUserIds.includes(user.id)) { + this.removeUser(user.id) + event.target.classList.remove('selected') + } else { + this.addUser(user) + event.target.classList.add('selected') + } + }, + addUser (user) { + this.selectedUserIds.push(user.id) + }, + removeUser (userId) { + this.selectedUserIds = this.selectedUserIds.filter(id => id !== userId) + }, + search (query) { + if (!query) { + this.loading = false + return + } + + this.loading = true + this.userIds = [] + this.$store.dispatch('search', { q: query, resolve: true, type: 'accounts', following: true }) + .then(data => { + this.loading = false + this.userIds = data.accounts.map(a => a.id) + }) + }, + createList () { + // the API has two different endpoints for "creating a list with a name" + // and "updating the accounts on the list". + this.$store.state.api.backendInteractor.createList({ title: this.title }) + .then((data) => { + this.$store.state.api.backendInteractor.addAccountsToList({ + id: data.id, accountIds: this.selectedUserIds + }) + this.$router.push({ name: 'list-timeline', params: { id: data.id } }) + }) + } + } +} + +export default ListNew diff --git a/src/components/list_new/list_new.vue b/src/components/list_new/list_new.vue new file mode 100644 index 00000000..952f7732 --- /dev/null +++ b/src/components/list_new/list_new.vue @@ -0,0 +1,100 @@ + + + + + diff --git a/src/components/lists/lists.js b/src/components/lists/lists.js index bb67fafb..f511f0f5 100644 --- a/src/components/lists/lists.js +++ b/src/components/lists/lists.js @@ -1,8 +1,15 @@ import ListCard from '../list_card/list_card.vue' +import ListNew from '../list_new/list_new.vue' const Lists = { + data () { + return { + isNew: false + } + }, components: { - ListCard + ListCard, + ListNew }, created () { this.$store.dispatch('startFetchingLists') @@ -11,6 +18,14 @@ const Lists = { lists () { return this.$store.state.api.lists } + }, + methods: { + cancelNewList () { + this.isNew = false + }, + newList () { + this.isNew = true + } } } diff --git a/src/components/lists/lists.vue b/src/components/lists/lists.vue index 20dd46d8..f11a2a02 100644 --- a/src/components/lists/lists.vue +++ b/src/components/lists/lists.vue @@ -1,9 +1,21 @@