forked from AkkomaGang/akkoma-fe
Merge branch '227-quick-add' into 'develop'
Support user searching to mute/block directly in the mutes/blocks tab See merge request pleroma/pleroma-fe!727
This commit is contained in:
commit
efa93d0829
9 changed files with 166 additions and 3 deletions
|
@ -27,6 +27,7 @@
|
||||||
"popper.js": "^1.14.7",
|
"popper.js": "^1.14.7",
|
||||||
"sanitize-html": "^1.13.0",
|
"sanitize-html": "^1.13.0",
|
||||||
"sass-loader": "^4.0.2",
|
"sass-loader": "^4.0.2",
|
||||||
|
"v-click-outside": "^2.1.1",
|
||||||
"vue": "^2.5.13",
|
"vue": "^2.5.13",
|
||||||
"vue-chat-scroll": "^1.2.1",
|
"vue-chat-scroll": "^1.2.1",
|
||||||
"vue-compose": "^0.7.1",
|
"vue-compose": "^0.7.1",
|
||||||
|
|
52
src/components/autosuggest/autosuggest.js
Normal file
52
src/components/autosuggest/autosuggest.js
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
const debounceMilliseconds = 500
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
query: { // function to query results and return a promise
|
||||||
|
type: Function,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
filter: { // function to filter results in real time
|
||||||
|
type: Function
|
||||||
|
},
|
||||||
|
placeholder: {
|
||||||
|
type: String,
|
||||||
|
default: 'Search...'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
term: '',
|
||||||
|
timeout: null,
|
||||||
|
results: [],
|
||||||
|
resultsVisible: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
filtered () {
|
||||||
|
return this.filter ? this.filter(this.results) : this.results
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
term (val) {
|
||||||
|
this.fetchResults(val)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
fetchResults (term) {
|
||||||
|
clearTimeout(this.timeout)
|
||||||
|
this.timeout = setTimeout(() => {
|
||||||
|
this.results = []
|
||||||
|
if (term) {
|
||||||
|
this.query(term).then((results) => { this.results = results })
|
||||||
|
}
|
||||||
|
}, debounceMilliseconds)
|
||||||
|
},
|
||||||
|
onInputClick () {
|
||||||
|
this.resultsVisible = true
|
||||||
|
},
|
||||||
|
onClickOutside () {
|
||||||
|
this.resultsVisible = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
45
src/components/autosuggest/autosuggest.vue
Normal file
45
src/components/autosuggest/autosuggest.vue
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
<template>
|
||||||
|
<div class="autosuggest" v-click-outside="onClickOutside">
|
||||||
|
<input v-model="term" :placeholder="placeholder" @click="onInputClick" class="autosuggest-input" />
|
||||||
|
<div class="autosuggest-results" v-if="resultsVisible && filtered.length > 0">
|
||||||
|
<slot v-for="item in filtered" :item="item" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script src="./autosuggest.js"></script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
@import '../../_variables.scss';
|
||||||
|
|
||||||
|
.autosuggest {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&-input {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-results {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 100%;
|
||||||
|
right: 0;
|
||||||
|
max-height: 400px;
|
||||||
|
background-color: $fallback--lightBg;
|
||||||
|
background-color: var(--lightBg, $fallback--lightBg);
|
||||||
|
border-style: solid;
|
||||||
|
border-width: 1px;
|
||||||
|
border-color: $fallback--border;
|
||||||
|
border-color: var(--border, $fallback--border);
|
||||||
|
border-radius: $fallback--inputRadius;
|
||||||
|
border-radius: var(--inputRadius, $fallback--inputRadius);
|
||||||
|
border-top-left-radius: 0;
|
||||||
|
border-top-right-radius: 0;
|
||||||
|
box-shadow: 1px 1px 4px rgba(0, 0, 0, 0.6);
|
||||||
|
box-shadow: var(--panelShadow);
|
||||||
|
overflow-y: auto;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -1,6 +1,8 @@
|
||||||
import { compose } from 'vue-compose'
|
import { compose } from 'vue-compose'
|
||||||
import unescape from 'lodash/unescape'
|
import unescape from 'lodash/unescape'
|
||||||
import get from 'lodash/get'
|
import get from 'lodash/get'
|
||||||
|
import map from 'lodash/map'
|
||||||
|
import reject from 'lodash/reject'
|
||||||
import TabSwitcher from '../tab_switcher/tab_switcher.js'
|
import TabSwitcher from '../tab_switcher/tab_switcher.js'
|
||||||
import ImageCropper from '../image_cropper/image_cropper.vue'
|
import ImageCropper from '../image_cropper/image_cropper.vue'
|
||||||
import StyleSwitcher from '../style_switcher/style_switcher.vue'
|
import StyleSwitcher from '../style_switcher/style_switcher.vue'
|
||||||
|
@ -9,8 +11,10 @@ import fileSizeFormatService from '../../services/file_size_format/file_size_for
|
||||||
import BlockCard from '../block_card/block_card.vue'
|
import BlockCard from '../block_card/block_card.vue'
|
||||||
import MuteCard from '../mute_card/mute_card.vue'
|
import MuteCard from '../mute_card/mute_card.vue'
|
||||||
import EmojiInput from '../emoji-input/emoji-input.vue'
|
import EmojiInput from '../emoji-input/emoji-input.vue'
|
||||||
|
import Autosuggest from '../autosuggest/autosuggest.vue'
|
||||||
import withSubscription from '../../hocs/with_subscription/with_subscription'
|
import withSubscription from '../../hocs/with_subscription/with_subscription'
|
||||||
import withList from '../../hocs/with_list/with_list'
|
import withList from '../../hocs/with_list/with_list'
|
||||||
|
import userSearchApi from '../../services/new_api/user_search.js'
|
||||||
|
|
||||||
const BlockList = compose(
|
const BlockList = compose(
|
||||||
withSubscription({
|
withSubscription({
|
||||||
|
@ -73,7 +77,10 @@ const UserSettings = {
|
||||||
ImageCropper,
|
ImageCropper,
|
||||||
BlockList,
|
BlockList,
|
||||||
MuteList,
|
MuteList,
|
||||||
EmojiInput
|
EmojiInput,
|
||||||
|
Autosuggest,
|
||||||
|
BlockCard,
|
||||||
|
MuteCard
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
user () {
|
user () {
|
||||||
|
@ -334,6 +341,25 @@ const UserSettings = {
|
||||||
if (window.confirm(`${this.$i18n.t('settings.revoke_token')}?`)) {
|
if (window.confirm(`${this.$i18n.t('settings.revoke_token')}?`)) {
|
||||||
this.$store.dispatch('revokeToken', id)
|
this.$store.dispatch('revokeToken', id)
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
filterUnblockedUsers (userIds) {
|
||||||
|
return reject(userIds, (userId) => {
|
||||||
|
const user = this.$store.getters.findUser(userId)
|
||||||
|
return !user || user.statusnet_blocking || user.id === this.$store.state.users.currentUser.id
|
||||||
|
})
|
||||||
|
},
|
||||||
|
filterUnMutedUsers (userIds) {
|
||||||
|
return reject(userIds, (userId) => {
|
||||||
|
const user = this.$store.getters.findUser(userId)
|
||||||
|
return !user || user.muted || user.id === this.$store.state.users.currentUser.id
|
||||||
|
})
|
||||||
|
},
|
||||||
|
queryUserIds (query) {
|
||||||
|
return userSearchApi.search({query, store: this.$store})
|
||||||
|
.then((users) => {
|
||||||
|
this.$store.dispatch('addNewUsers', users)
|
||||||
|
return map(users, 'id')
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
<div class="setting-item" >
|
<div class="setting-item" >
|
||||||
<h2>{{$t('settings.name_bio')}}</h2>
|
<h2>{{$t('settings.name_bio')}}</h2>
|
||||||
<p>{{$t('settings.name')}}</p>
|
<p>{{$t('settings.name')}}</p>
|
||||||
<EmojiInput
|
<EmojiInput
|
||||||
type="text"
|
type="text"
|
||||||
v-model="newName"
|
v-model="newName"
|
||||||
id="username"
|
id="username"
|
||||||
|
@ -195,12 +195,22 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div :label="$t('settings.blocks_tab')">
|
<div :label="$t('settings.blocks_tab')">
|
||||||
|
<div class="profile-edit-usersearch-wrapper">
|
||||||
|
<Autosuggest :filter="filterUnblockedUsers" :query="queryUserIds" :placeholder="$t('settings.search_user_to_block')">
|
||||||
|
<BlockCard slot-scope="row" :userId="row.item"/>
|
||||||
|
</Autosuggest>
|
||||||
|
</div>
|
||||||
<block-list :refresh="true">
|
<block-list :refresh="true">
|
||||||
<template slot="empty">{{$t('settings.no_blocks')}}</template>
|
<template slot="empty">{{$t('settings.no_blocks')}}</template>
|
||||||
</block-list>
|
</block-list>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div :label="$t('settings.mutes_tab')">
|
<div :label="$t('settings.mutes_tab')">
|
||||||
|
<div class="profile-edit-usersearch-wrapper">
|
||||||
|
<Autosuggest :filter="filterUnMutedUsers" :query="queryUserIds" :placeholder="$t('settings.search_user_to_mute')">
|
||||||
|
<MuteCard slot-scope="row" :userId="row.item"/>
|
||||||
|
</Autosuggest>
|
||||||
|
</div>
|
||||||
<mute-list :refresh="true">
|
<mute-list :refresh="true">
|
||||||
<template slot="empty">{{$t('settings.no_mutes')}}</template>
|
<template slot="empty">{{$t('settings.no_mutes')}}</template>
|
||||||
</mute-list>
|
</mute-list>
|
||||||
|
@ -262,5 +272,9 @@
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&-usersearch-wrapper {
|
||||||
|
padding: 1em;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -217,6 +217,8 @@
|
||||||
"reply_visibility_self": "Only show replies directed at me",
|
"reply_visibility_self": "Only show replies directed at me",
|
||||||
"saving_err": "Error saving settings",
|
"saving_err": "Error saving settings",
|
||||||
"saving_ok": "Settings saved",
|
"saving_ok": "Settings saved",
|
||||||
|
"search_user_to_block": "Search whom you want to block",
|
||||||
|
"search_user_to_mute": "Search whom you want to mute",
|
||||||
"security_tab": "Security",
|
"security_tab": "Security",
|
||||||
"scope_copy": "Copy scope when replying (DMs are always copied)",
|
"scope_copy": "Copy scope when replying (DMs are always copied)",
|
||||||
"minimal_scopes_mode": "Minimize post scope selection options",
|
"minimal_scopes_mode": "Minimize post scope selection options",
|
||||||
|
|
|
@ -22,6 +22,7 @@ import pushNotifications from './lib/push_notifications_plugin.js'
|
||||||
import messages from './i18n/messages.js'
|
import messages from './i18n/messages.js'
|
||||||
|
|
||||||
import VueChatScroll from 'vue-chat-scroll'
|
import VueChatScroll from 'vue-chat-scroll'
|
||||||
|
import VueClickOutside from 'v-click-outside'
|
||||||
|
|
||||||
import afterStoreSetup from './boot/after_store.js'
|
import afterStoreSetup from './boot/after_store.js'
|
||||||
|
|
||||||
|
@ -39,6 +40,7 @@ Vue.use(VueTimeago, {
|
||||||
})
|
})
|
||||||
Vue.use(VueI18n)
|
Vue.use(VueI18n)
|
||||||
Vue.use(VueChatScroll)
|
Vue.use(VueChatScroll)
|
||||||
|
Vue.use(VueClickOutside)
|
||||||
|
|
||||||
const i18n = new VueI18n({
|
const i18n = new VueI18n({
|
||||||
// By default, use the browser locale, we will update it if neccessary
|
// By default, use the browser locale, we will update it if neccessary
|
||||||
|
|
|
@ -132,6 +132,11 @@ export const mutations = {
|
||||||
saveBlockIds (state, blockIds) {
|
saveBlockIds (state, blockIds) {
|
||||||
state.currentUser.blockIds = blockIds
|
state.currentUser.blockIds = blockIds
|
||||||
},
|
},
|
||||||
|
addBlockId (state, blockId) {
|
||||||
|
if (state.currentUser.blockIds.indexOf(blockId) === -1) {
|
||||||
|
state.currentUser.blockIds.push(blockId)
|
||||||
|
}
|
||||||
|
},
|
||||||
updateMutes (state, mutedUsers) {
|
updateMutes (state, mutedUsers) {
|
||||||
// Reset muted of all fetched users
|
// Reset muted of all fetched users
|
||||||
each(state.users, (user) => { user.muted = false })
|
each(state.users, (user) => { user.muted = false })
|
||||||
|
@ -140,6 +145,11 @@ export const mutations = {
|
||||||
saveMuteIds (state, muteIds) {
|
saveMuteIds (state, muteIds) {
|
||||||
state.currentUser.muteIds = muteIds
|
state.currentUser.muteIds = muteIds
|
||||||
},
|
},
|
||||||
|
addMuteId (state, muteId) {
|
||||||
|
if (state.currentUser.muteIds.indexOf(muteId) === -1) {
|
||||||
|
state.currentUser.muteIds.push(muteId)
|
||||||
|
}
|
||||||
|
},
|
||||||
setUserForStatus (state, status) {
|
setUserForStatus (state, status) {
|
||||||
status.user = state.usersObject[status.user.id]
|
status.user = state.usersObject[status.user.id]
|
||||||
},
|
},
|
||||||
|
@ -215,6 +225,7 @@ const users = {
|
||||||
return store.rootState.api.backendInteractor.blockUser(userId)
|
return store.rootState.api.backendInteractor.blockUser(userId)
|
||||||
.then((relationship) => {
|
.then((relationship) => {
|
||||||
store.commit('updateUserRelationship', [relationship])
|
store.commit('updateUserRelationship', [relationship])
|
||||||
|
store.commit('addBlockId', userId)
|
||||||
store.commit('removeStatus', { timeline: 'friends', userId })
|
store.commit('removeStatus', { timeline: 'friends', userId })
|
||||||
store.commit('removeStatus', { timeline: 'public', userId })
|
store.commit('removeStatus', { timeline: 'public', userId })
|
||||||
store.commit('removeStatus', { timeline: 'publicAndExternal', userId })
|
store.commit('removeStatus', { timeline: 'publicAndExternal', userId })
|
||||||
|
@ -234,7 +245,10 @@ const users = {
|
||||||
},
|
},
|
||||||
muteUser (store, id) {
|
muteUser (store, id) {
|
||||||
return store.rootState.api.backendInteractor.muteUser(id)
|
return store.rootState.api.backendInteractor.muteUser(id)
|
||||||
.then((relationship) => store.commit('updateUserRelationship', [relationship]))
|
.then((relationship) => {
|
||||||
|
store.commit('updateUserRelationship', [relationship])
|
||||||
|
store.commit('addMuteId', id)
|
||||||
|
})
|
||||||
},
|
},
|
||||||
unmuteUser (store, id) {
|
unmuteUser (store, id) {
|
||||||
return store.rootState.api.backendInteractor.unmuteUser(id)
|
return store.rootState.api.backendInteractor.unmuteUser(id)
|
||||||
|
@ -281,6 +295,9 @@ const users = {
|
||||||
|
|
||||||
unregisterPushNotifications(token)
|
unregisterPushNotifications(token)
|
||||||
},
|
},
|
||||||
|
addNewUsers ({ commit }, users) {
|
||||||
|
commit('addNewUsers', users)
|
||||||
|
},
|
||||||
addNewStatuses (store, { statuses }) {
|
addNewStatuses (store, { statuses }) {
|
||||||
const users = map(statuses, 'user')
|
const users = map(statuses, 'user')
|
||||||
const retweetedUsers = compact(map(statuses, 'retweeted_status.user'))
|
const retweetedUsers = compact(map(statuses, 'retweeted_status.user'))
|
||||||
|
|
|
@ -6389,6 +6389,10 @@ uuid@^3.3.2:
|
||||||
version "3.3.2"
|
version "3.3.2"
|
||||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131"
|
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131"
|
||||||
|
|
||||||
|
v-click-outside@^2.1.1:
|
||||||
|
version "2.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/v-click-outside/-/v-click-outside-2.1.1.tgz#5af80b68a1c82eac89c597890434fa3994b42ed1"
|
||||||
|
|
||||||
validate-npm-package-license@^3.0.1:
|
validate-npm-package-license@^3.0.1:
|
||||||
version "3.0.4"
|
version "3.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a"
|
resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a"
|
||||||
|
|
Loading…
Reference in a new issue