logo

pleroma-fe

My custom branche(s) on git.pleroma.social/pleroma/pleroma-fe git clone https://hacktivis.me/git/pleroma-fe.git
commit: 50f5afbce1f2bc4dbd0ddf6c951c7e519dfc6ce3
parent 14292d7ed12a806efcf766895bc1c3aa56fd53f8
Author: Henry Jameson <me@hjkos.com>
Date:   Mon, 15 Aug 2022 23:19:33 +0300

add and remove users to/from lists from their profile

Diffstat:

Msrc/components/account_actions/account_actions.js4+++-
Msrc/components/account_actions/account_actions.vue1+
Msrc/components/lists_edit/lists_edit.js10+++++-----
Msrc/components/lists_new/lists_new.js4++--
Msrc/components/popover/popover.js25+++++++++++++++++++++++--
Msrc/i18n/en.json3++-
Msrc/modules/lists.js97++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------
Msrc/modules/users.js9+++++++++
Msrc/services/api/api.service.js35++++++++++++++++++++++-------------
Msrc/services/entity_normalizer/entity_normalizer.service.js1+
10 files changed, 134 insertions(+), 55 deletions(-)

diff --git a/src/components/account_actions/account_actions.js b/src/components/account_actions/account_actions.js @@ -1,6 +1,7 @@ import { mapState } from 'vuex' import ProgressButton from '../progress_button/progress_button.vue' import Popover from '../popover/popover.vue' +import UserListMenu from 'src/components/user_list_menu/user_list_menu.vue' import { library } from '@fortawesome/fontawesome-svg-core' import { faEllipsisV @@ -19,7 +20,8 @@ const AccountActions = { }, components: { ProgressButton, - Popover + Popover, + UserListMenu }, methods: { showRepeats () { diff --git a/src/components/account_actions/account_actions.vue b/src/components/account_actions/account_actions.vue @@ -28,6 +28,7 @@ class="dropdown-divider" /> </template> + <UserListMenu :user="user" /> <button v-if="relationship.blocking" class="btn button-default btn-block dropdown-item" diff --git a/src/components/lists_edit/lists_edit.js b/src/components/lists_edit/lists_edit.js @@ -27,9 +27,9 @@ const ListsNew = { } }, created () { - this.$store.dispatch('fetchList', { id: this.id }) + this.$store.dispatch('fetchList', { listId: this.id }) .then(() => { this.title = this.findListTitle(this.id) }) - this.$store.dispatch('fetchListAccounts', { id: this.id }) + this.$store.dispatch('fetchListAccounts', { listId: this.id }) .then(() => { this.selectedUserIds = this.findListAccounts(this.id) this.selectedUserIds.forEach(userId => { @@ -76,13 +76,13 @@ const ListsNew = { this.userIds = results }, updateList () { - this.$store.dispatch('setList', { id: this.id, title: this.title }) - this.$store.dispatch('setListAccounts', { id: this.id, accountIds: this.selectedUserIds }) + this.$store.dispatch('setList', { listId: this.id, title: this.title }) + this.$store.dispatch('setListAccounts', { listId: this.id, accountIds: this.selectedUserIds }) this.$router.push({ name: 'lists-timeline', params: { id: this.id } }) }, deleteList () { - this.$store.dispatch('deleteList', { id: this.id }) + this.$store.dispatch('deleteList', { listId: this.id }) this.$router.push({ name: 'lists' }) } } diff --git a/src/components/lists_new/lists_new.js b/src/components/lists_new/lists_new.js @@ -69,8 +69,8 @@ const ListsNew = { // and "updating the accounts on the list". this.$store.dispatch('createList', { title: this.title }) .then((list) => { - this.$store.dispatch('setListAccounts', { id: list.id, accountIds: this.selectedUserIds }) - this.$router.push({ name: 'lists-timeline', params: { id: list.id } }) + this.$store.dispatch('setListAccounts', { listId: list.id, accountIds: this.selectedUserIds }) + this.$router.push({ name: 'lists-timeline', params: { listId: list.id } }) }) } } diff --git a/src/components/popover/popover.js b/src/components/popover/popover.js @@ -4,7 +4,7 @@ const Popover = { // Action to trigger popover: either 'hover' or 'click' trigger: String, - // Either 'top' or 'bottom' + // 'top', 'bottom', 'left', 'right' placement: String, // Takes object with properties 'x' and 'y', values of these can be @@ -84,6 +84,8 @@ const Popover = { const anchorStyle = getComputedStyle(anchorEl) const topPadding = parseFloat(anchorStyle.paddingTop) const bottomPadding = parseFloat(anchorStyle.paddingBottom) + const rightPadding = parseFloat(anchorStyle.paddingRight) + const leftPadding = parseFloat(anchorStyle.paddingLeft) // Screen position of the origin point for popover = center of the anchor const origin = { @@ -170,7 +172,7 @@ const Popover = { if (overlayCenter) { translateX = origin.x + horizOffset translateY = origin.y + vertOffset - } else { + } else if (this.placement !== 'right' && this.placement !== 'left') { // Default to whatever user wished with placement prop let usingTop = this.placement !== 'bottom' @@ -189,6 +191,25 @@ const Popover = { const xOffset = (this.offset && this.offset.x) || 0 translateX = origin.x + horizOffset + xOffset + } else { + // Default to whatever user wished with placement prop + let usingRight = this.placement !== 'left' + + // Handle special cases, first force to displaying on top if there's not space on bottom, + // regardless of what placement value was. Then check if there's not space on top, and + // force to bottom, again regardless of what placement value was. + const rightBoundary = origin.x - anchorWidth * 0.5 + (this.removePadding ? rightPadding : 0) + const leftBoundary = origin.x + anchorWidth * 0.5 - (this.removePadding ? leftPadding : 0) + if (leftBoundary + content.offsetWidth > xBounds.max) usingRight = true + if (rightBoundary - content.offsetWidth < xBounds.min) usingRight = false + + const xOffset = (this.offset && this.offset.x) || 0 + translateX = usingRight + ? rightBoundary - xOffset - content.offsetWidth + : leftBoundary + xOffset + + const yOffset = (this.offset && this.offset.y) || 0 + translateY = origin.y + vertOffset + yOffset } this.styles = { diff --git a/src/i18n/en.json b/src/i18n/en.json @@ -980,7 +980,8 @@ "create": "Create", "save": "Save changes", "delete": "Delete list", - "following_only": "Limit to Following" + "following_only": "Limit to Following", + "manage_lists": "Manage lists" }, "file_type": { "audio": "Audio", diff --git a/src/modules/lists.js b/src/modules/lists.js @@ -9,27 +9,42 @@ export const mutations = { setLists (state, value) { state.allLists = value }, - setList (state, { id, title }) { - if (!state.allListsObject[id]) { - state.allListsObject[id] = {} + setList (state, { listId, title }) { + if (!state.allListsObject[listId]) { + state.allListsObject[listId] = { accountIds: [] } } - state.allListsObject[id].title = title + state.allListsObject[listId].title = title - if (!find(state.allLists, { id })) { - state.allLists.push({ id, title }) + if (!find(state.allLists, { listId })) { + state.allLists.push({ listId, title }) } else { - find(state.allLists, { id }).title = title + find(state.allLists, { listId }).title = title } }, - setListAccounts (state, { id, accountIds }) { - if (!state.allListsObject[id]) { - state.allListsObject[id] = {} + setListAccounts (state, { listId, accountIds }) { + if (!state.allListsObject[listId]) { + state.allListsObject[listId] = { accountIds: [] } } - state.allListsObject[id].accountIds = accountIds + state.allListsObject[listId].accountIds = accountIds }, - deleteList (state, { id }) { - delete state.allListsObject[id] - remove(state.allLists, list => list.id === id) + addListAccount (state, { listId, accountId }) { + if (!state.allListsObject[listId]) { + state.allListsObject[listId] = { accountIds: [] } + } + state.allListsObject[listId].accountIds.push(accountId) + }, + removeListAccount (state, { listId, accountId }) { + if (!state.allListsObject[listId]) { + state.allListsObject[listId] = { accountIds: [] } + } + const { accountIds } = state.allListsObject[listId] + const set = new Set(accountIds) + set.delete(accountId) + state.allListsObject[listId].accountIds = [...set] + }, + deleteList (state, { listId }) { + delete state.allListsObject[listId] + remove(state.allLists, list => list.id === listId) } } @@ -40,37 +55,57 @@ const actions = { createList ({ rootState, commit }, { title }) { return rootState.api.backendInteractor.createList({ title }) .then((list) => { - commit('setList', { id: list.id, title }) + commit('setList', { listId: list.id, title }) return list }) }, - fetchList ({ rootState, commit }, { id }) { - return rootState.api.backendInteractor.getList({ id }) + fetchList ({ rootState, commit }, { listId }) { + return rootState.api.backendInteractor.getList({ listId }) .then((list) => commit('setList', { id: list.id, title: list.title })) }, - fetchListAccounts ({ rootState, commit }, { id }) { - return rootState.api.backendInteractor.getListAccounts({ id }) - .then((accountIds) => commit('setListAccounts', { id, accountIds })) + fetchListAccounts ({ rootState, commit }, { listId }) { + return rootState.api.backendInteractor.getListAccounts({ listId }) + .then((accountIds) => commit('setListAccounts', { listId, accountIds })) }, - setList ({ rootState, commit }, { id, title }) { - rootState.api.backendInteractor.updateList({ id, title }) - commit('setList', { id, title }) + setList ({ rootState, commit }, { listId, title }) { + rootState.api.backendInteractor.updateList({ listId, title }) + commit('setList', { listId, title }) }, - setListAccounts ({ rootState, commit }, { id, accountIds }) { - const saved = rootState.lists.allListsObject[id].accountIds || [] + setListAccounts ({ rootState, commit }, { listId, accountIds }) { + const saved = rootState.lists.allListsObject[listId].accountIds || [] const added = accountIds.filter(id => !saved.includes(id)) const removed = saved.filter(id => !accountIds.includes(id)) - commit('setListAccounts', { id, accountIds }) + commit('setListAccounts', { listId, accountIds }) if (added.length > 0) { - rootState.api.backendInteractor.addAccountsToList({ id, accountIds: added }) + rootState.api.backendInteractor.addAccountsToList({ listId, accountIds: added }) } if (removed.length > 0) { - rootState.api.backendInteractor.removeAccountsFromList({ id, accountIds: removed }) + rootState.api.backendInteractor.removeAccountsFromList({ listId, accountIds: removed }) } }, - deleteList ({ rootState, commit }, { id }) { - rootState.api.backendInteractor.deleteList({ id }) - commit('deleteList', { id }) + addListAccount ({ rootState, commit }, { listId, accountId }) { + return rootState + .api + .backendInteractor + .addAccountsToList({ listId, accountIds: [accountId] }) + .then((result) => { + commit('addListAccount', { listId, accountId }) + return result + }) + }, + removeListAccount ({ rootState, commit }, { listId, accountId }) { + return rootState + .api + .backendInteractor + .removeAccountsFromList({ listId, accountIds: [accountId] }) + .then((result) => { + commit('removeListAccount', { listId, accountId }) + return result + }) + }, + deleteList ({ rootState, commit }, { listId }) { + rootState.api.backendInteractor.deleteList({ listId }) + commit('deleteList', { listId }) } } diff --git a/src/modules/users.js b/src/modules/users.js @@ -170,6 +170,9 @@ export const mutations = { state.relationships[relationship.id] = relationship }) }, + updateUserInLists (state, { id, inLists }) { + state.usersObject[id].inLists = inLists + }, saveBlockIds (state, blockIds) { state.currentUser.blockIds = blockIds }, @@ -291,6 +294,12 @@ const users = { .then((relationships) => store.commit('updateUserRelationship', relationships)) } }, + fetchUserInLists (store, id) { + if (store.state.currentUser) { + store.rootState.api.backendInteractor.fetchUserInLists({ id }) + .then((inLists) => store.commit('updateUserInLists', { id, inLists })) + } + }, fetchBlocks (store) { return store.rootState.api.backendInteractor.fetchBlocks() .then((blocks) => { diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js @@ -52,6 +52,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_USER_IN_LISTS = id => `/api/v1/accounts/${id}/lists` const MASTODON_LIST_URL = id => `/api/v1/lists/${id}` const MASTODON_LIST_TIMELINE_URL = id => `/api/v1/timelines/list/${id}` const MASTODON_LIST_ACCOUNTS_URL = id => `/api/v1/lists/${id}/accounts` @@ -262,6 +263,13 @@ const unfollowUser = ({ id, credentials }) => { }).then((data) => data.json()) } +const fetchUserInLists = ({ id, credentials }) => { + const url = MASTODON_USER_IN_LISTS(id) + return fetch(url, { + headers: authHeaders(credentials) + }).then((data) => data.json()) +} + const pinOwnStatus = ({ id, credentials }) => { return promisedRequest({ url: MASTODON_PIN_OWN_STATUS(id), credentials, method: 'POST' }) .then((data) => parseStatus(data)) @@ -408,14 +416,14 @@ const createList = ({ title, credentials }) => { }).then((data) => data.json()) } -const getList = ({ id, credentials }) => { - const url = MASTODON_LIST_URL(id) +const getList = ({ listId, credentials }) => { + const url = MASTODON_LIST_URL(listId) return fetch(url, { headers: authHeaders(credentials) }) .then((data) => data.json()) } -const updateList = ({ id, title, credentials }) => { - const url = MASTODON_LIST_URL(id) +const updateList = ({ listId, title, credentials }) => { + const url = MASTODON_LIST_URL(listId) const headers = authHeaders(credentials) headers['Content-Type'] = 'application/json' @@ -426,15 +434,15 @@ const updateList = ({ id, title, credentials }) => { }) } -const getListAccounts = ({ id, credentials }) => { - const url = MASTODON_LIST_ACCOUNTS_URL(id) +const getListAccounts = ({ listId, credentials }) => { + const url = MASTODON_LIST_ACCOUNTS_URL(listId) return fetch(url, { headers: authHeaders(credentials) }) .then((data) => data.json()) .then((data) => data.map(({ id }) => id)) } -const addAccountsToList = ({ id, accountIds, credentials }) => { - const url = MASTODON_LIST_ACCOUNTS_URL(id) +const addAccountsToList = ({ listId, accountIds, credentials }) => { + const url = MASTODON_LIST_ACCOUNTS_URL(listId) const headers = authHeaders(credentials) headers['Content-Type'] = 'application/json' @@ -445,8 +453,8 @@ const addAccountsToList = ({ id, accountIds, credentials }) => { }) } -const removeAccountsFromList = ({ id, accountIds, credentials }) => { - const url = MASTODON_LIST_ACCOUNTS_URL(id) +const removeAccountsFromList = ({ listId, accountIds, credentials }) => { + const url = MASTODON_LIST_ACCOUNTS_URL(listId) const headers = authHeaders(credentials) headers['Content-Type'] = 'application/json' @@ -457,8 +465,8 @@ const removeAccountsFromList = ({ id, accountIds, credentials }) => { }) } -const deleteList = ({ id, credentials }) => { - const url = MASTODON_LIST_URL(id) +const deleteList = ({ listId, credentials }) => { + const url = MASTODON_LIST_URL(listId) return fetch(url, { method: 'DELETE', headers: authHeaders(credentials) @@ -1563,7 +1571,8 @@ const apiService = { sendChatMessage, readChat, deleteChatMessage, - setReportState + setReportState, + fetchUserInLists } export default apiService diff --git a/src/services/entity_normalizer/entity_normalizer.service.js b/src/services/entity_normalizer/entity_normalizer.service.js @@ -43,6 +43,7 @@ export const parseUser = (data) => { // case for users in "mentions" property for statuses in MastoAPI const mastoShort = masto && !Object.prototype.hasOwnProperty.call(data, 'avatar') + output.inLists = null output.id = String(data.id) output._original = data // used for server-side settings