Merge branch 'develop' into 'feat/conversation-muting'
# Conflicts: # src/services/api/api.service.js
This commit is contained in:
commit
3370dd80dc
45 changed files with 717 additions and 387 deletions
|
@ -100,7 +100,7 @@
|
|||
<!-- eslint-disable vue/no-v-html -->
|
||||
<h1><a :href="attachment.url">{{ attachment.oembed.title }}</a></h1>
|
||||
<div v-html="attachment.oembed.oembedHTML" />
|
||||
<!-- eslint-enabled vue/no-v-html -->
|
||||
<!-- eslint-enable vue/no-v-html -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
<template>
|
||||
<div
|
||||
class="block"
|
||||
style="position: relative"
|
||||
>
|
||||
<div>
|
||||
<Popper
|
||||
trigger="click"
|
||||
append-to-body
|
||||
|
@ -131,6 +128,7 @@
|
|||
</div>
|
||||
<button
|
||||
slot="reference"
|
||||
class="btn btn-default btn-block"
|
||||
:class="{ pressed: showDropDown }"
|
||||
@click="toggleMenu"
|
||||
>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
:disabled="progress || disabled"
|
||||
@click="onClick"
|
||||
>
|
||||
<template v-if="progress">
|
||||
<template v-if="progress && $slots.progress">
|
||||
<slot name="progress" />
|
||||
</template>
|
||||
<template v-else>
|
||||
|
|
98
src/components/search/search.js
Normal file
98
src/components/search/search.js
Normal file
|
@ -0,0 +1,98 @@
|
|||
import FollowCard from '../follow_card/follow_card.vue'
|
||||
import Conversation from '../conversation/conversation.vue'
|
||||
import Status from '../status/status.vue'
|
||||
import map from 'lodash/map'
|
||||
|
||||
const Search = {
|
||||
components: {
|
||||
FollowCard,
|
||||
Conversation,
|
||||
Status
|
||||
},
|
||||
props: [
|
||||
'query'
|
||||
],
|
||||
data () {
|
||||
return {
|
||||
loaded: false,
|
||||
loading: false,
|
||||
searchTerm: this.query || '',
|
||||
userIds: [],
|
||||
statuses: [],
|
||||
hashtags: [],
|
||||
currenResultTab: 'statuses'
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
users () {
|
||||
return this.userIds.map(userId => this.$store.getters.findUser(userId))
|
||||
},
|
||||
visibleStatuses () {
|
||||
const allStatusesObject = this.$store.state.statuses.allStatusesObject
|
||||
|
||||
return this.statuses.filter(status =>
|
||||
allStatusesObject[status.id] && !allStatusesObject[status.id].deleted
|
||||
)
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.search(this.query)
|
||||
},
|
||||
watch: {
|
||||
query (newValue) {
|
||||
this.searchTerm = newValue
|
||||
this.search(newValue)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
newQuery (query) {
|
||||
this.$router.push({ name: 'search', query: { query } })
|
||||
this.$refs.searchInput.focus()
|
||||
},
|
||||
search (query) {
|
||||
if (!query) {
|
||||
this.loading = false
|
||||
return
|
||||
}
|
||||
|
||||
this.loading = true
|
||||
this.userIds = []
|
||||
this.statuses = []
|
||||
this.hashtags = []
|
||||
this.$refs.searchInput.blur()
|
||||
|
||||
this.$store.dispatch('search', { q: query, resolve: true })
|
||||
.then(data => {
|
||||
this.loading = false
|
||||
this.userIds = map(data.accounts, 'id')
|
||||
this.statuses = data.statuses
|
||||
this.hashtags = data.hashtags
|
||||
this.currenResultTab = this.getActiveTab()
|
||||
this.loaded = true
|
||||
})
|
||||
},
|
||||
resultCount (tabName) {
|
||||
const length = this[tabName].length
|
||||
return length === 0 ? '' : ` (${length})`
|
||||
},
|
||||
onResultTabSwitch (_index, dataset) {
|
||||
this.currenResultTab = dataset.filter
|
||||
},
|
||||
getActiveTab () {
|
||||
if (this.visibleStatuses.length > 0) {
|
||||
return 'statuses'
|
||||
} else if (this.users.length > 0) {
|
||||
return 'people'
|
||||
} else if (this.hashtags.length > 0) {
|
||||
return 'hashtags'
|
||||
}
|
||||
|
||||
return 'statuses'
|
||||
},
|
||||
lastHistoryRecord (hashtag) {
|
||||
return hashtag.history && hashtag.history[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default Search
|
211
src/components/search/search.vue
Normal file
211
src/components/search/search.vue
Normal file
|
@ -0,0 +1,211 @@
|
|||
<template>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<div class="title">
|
||||
{{ $t('nav.search') }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="search-input-container">
|
||||
<input
|
||||
ref="searchInput"
|
||||
v-model="searchTerm"
|
||||
class="search-input"
|
||||
:placeholder="$t('nav.search')"
|
||||
@keyup.enter="newQuery(searchTerm)"
|
||||
>
|
||||
<button
|
||||
class="btn search-button"
|
||||
@click="newQuery(searchTerm)"
|
||||
>
|
||||
<i class="icon-search" />
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
v-if="loading"
|
||||
class="text-center loading-icon"
|
||||
>
|
||||
<i class="icon-spin3 animate-spin" />
|
||||
</div>
|
||||
<div v-else-if="loaded">
|
||||
<div class="search-nav-heading">
|
||||
<tab-switcher
|
||||
ref="tabSwitcher"
|
||||
:on-switch="onResultTabSwitch"
|
||||
:custom-active="currenResultTab"
|
||||
>
|
||||
<span
|
||||
data-tab-dummy
|
||||
data-filter="statuses"
|
||||
:label="$t('user_card.statuses') + resultCount('visibleStatuses')"
|
||||
/>
|
||||
<span
|
||||
data-tab-dummy
|
||||
data-filter="people"
|
||||
:label="$t('search.people') + resultCount('users')"
|
||||
/>
|
||||
<span
|
||||
data-tab-dummy
|
||||
data-filter="hashtags"
|
||||
:label="$t('search.hashtags') + resultCount('hashtags')"
|
||||
/>
|
||||
</tab-switcher>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div v-if="currenResultTab === 'statuses'">
|
||||
<div
|
||||
v-if="visibleStatuses.length === 0 && !loading && loaded"
|
||||
class="search-result-heading"
|
||||
>
|
||||
<h4>{{ $t('search.no_results') }}</h4>
|
||||
</div>
|
||||
<Status
|
||||
v-for="status in visibleStatuses"
|
||||
:key="status.id"
|
||||
:collapsable="false"
|
||||
:expandable="false"
|
||||
:compact="false"
|
||||
class="search-result"
|
||||
:statusoid="status"
|
||||
:no-heading="false"
|
||||
/>
|
||||
</div>
|
||||
<div v-else-if="currenResultTab === 'people'">
|
||||
<div
|
||||
v-if="users.length === 0 && !loading && loaded"
|
||||
class="search-result-heading"
|
||||
>
|
||||
<h4>{{ $t('search.no_results') }}</h4>
|
||||
</div>
|
||||
<FollowCard
|
||||
v-for="user in users"
|
||||
:key="user.id"
|
||||
:user="user"
|
||||
class="list-item search-result"
|
||||
/>
|
||||
</div>
|
||||
<div v-else-if="currenResultTab === 'hashtags'">
|
||||
<div
|
||||
v-if="hashtags.length === 0 && !loading && loaded"
|
||||
class="search-result-heading"
|
||||
>
|
||||
<h4>{{ $t('search.no_results') }}</h4>
|
||||
</div>
|
||||
<div
|
||||
v-for="hashtag in hashtags"
|
||||
:key="hashtag.url"
|
||||
class="status trend search-result"
|
||||
>
|
||||
<div class="hashtag">
|
||||
<router-link :to="{ name: 'tag-timeline', params: { tag: hashtag.name } }">
|
||||
#{{ hashtag.name }}
|
||||
</router-link>
|
||||
<div v-if="lastHistoryRecord(hashtag)">
|
||||
<span v-if="lastHistoryRecord(hashtag).accounts == 1">
|
||||
{{ $t('search.person_talking', { count: lastHistoryRecord(hashtag).accounts }) }}
|
||||
</span>
|
||||
<span v-else>
|
||||
{{ $t('search.people_talking', { count: lastHistoryRecord(hashtag).accounts }) }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="lastHistoryRecord(hashtag)"
|
||||
class="count"
|
||||
>
|
||||
{{ lastHistoryRecord(hashtag).uses }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="search-result-footer text-center panel-footer faint" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script src="./search.js"></script>
|
||||
|
||||
<style lang="scss">
|
||||
@import '../../_variables.scss';
|
||||
|
||||
.search-result-heading {
|
||||
color: $fallback--faint;
|
||||
color: var(--faint, $fallback--faint);
|
||||
padding: 0.75rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@media all and (max-width: 800px) {
|
||||
.search-nav-heading {
|
||||
.tab-switcher .tabs .tab-wrapper {
|
||||
display: block;
|
||||
justify-content: center;
|
||||
flex: 1 1 auto;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.search-result {
|
||||
box-sizing: border-box;
|
||||
border-bottom: 1px solid;
|
||||
border-color: $fallback--border;
|
||||
border-color: var(--border, $fallback--border);
|
||||
}
|
||||
|
||||
.search-result-footer {
|
||||
border-width: 1px 0 0 0;
|
||||
border-style: solid;
|
||||
border-color: var(--border, $fallback--border);
|
||||
padding: 10px;
|
||||
background-color: $fallback--fg;
|
||||
background-color: var(--panel, $fallback--fg);
|
||||
}
|
||||
|
||||
.search-input-container {
|
||||
padding: 0.8rem;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
.search-input {
|
||||
width: 100%;
|
||||
line-height: 1.125rem;
|
||||
font-size: 1rem;
|
||||
padding: 0.5rem;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.search-button {
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
}
|
||||
|
||||
.loading-icon {
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
.trend {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.hashtag {
|
||||
flex: 1 1 auto;
|
||||
color: $fallback--text;
|
||||
color: var(--text, $fallback--text);
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.count {
|
||||
flex: 0 0 auto;
|
||||
width: 2rem;
|
||||
font-size: 1.5rem;
|
||||
line-height: 2.25rem;
|
||||
font-weight: 500;
|
||||
text-align: center;
|
||||
color: $fallback--text;
|
||||
color: var(--text, $fallback--text);
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
27
src/components/search_bar/search_bar.js
Normal file
27
src/components/search_bar/search_bar.js
Normal file
|
@ -0,0 +1,27 @@
|
|||
const SearchBar = {
|
||||
data: () => ({
|
||||
searchTerm: undefined,
|
||||
hidden: true,
|
||||
error: false,
|
||||
loading: false
|
||||
}),
|
||||
watch: {
|
||||
'$route': function (route) {
|
||||
if (route.name === 'search') {
|
||||
this.searchTerm = route.query.query
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
find (searchTerm) {
|
||||
this.$router.push({ name: 'search', query: { query: searchTerm } })
|
||||
this.$refs.searchInput.focus()
|
||||
},
|
||||
toggleHidden () {
|
||||
this.hidden = !this.hidden
|
||||
this.$emit('toggled', this.hidden)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default SearchBar
|
|
@ -1,36 +1,36 @@
|
|||
<template>
|
||||
<div>
|
||||
<div class="user-finder-container">
|
||||
<div class="search-bar-container">
|
||||
<i
|
||||
v-if="loading"
|
||||
class="icon-spin4 user-finder-icon animate-spin-slow"
|
||||
class="icon-spin4 finder-icon animate-spin-slow"
|
||||
/>
|
||||
<a
|
||||
v-if="hidden"
|
||||
href="#"
|
||||
:title="$t('finder.find_user')"
|
||||
:title="$t('nav.search')"
|
||||
><i
|
||||
class="icon-user-plus user-finder-icon"
|
||||
class="button-icon icon-search"
|
||||
@click.prevent.stop="toggleHidden"
|
||||
/></a>
|
||||
<template v-else>
|
||||
<input
|
||||
id="user-finder-input"
|
||||
ref="userSearchInput"
|
||||
v-model="username"
|
||||
class="user-finder-input"
|
||||
:placeholder="$t('finder.find_user')"
|
||||
id="search-bar-input"
|
||||
ref="searchInput"
|
||||
v-model="searchTerm"
|
||||
class="search-bar-input"
|
||||
:placeholder="$t('nav.search')"
|
||||
type="text"
|
||||
@keyup.enter="findUser(username)"
|
||||
@keyup.enter="find(searchTerm)"
|
||||
>
|
||||
<button
|
||||
class="btn search-button"
|
||||
@click="findUser(username)"
|
||||
@click="find(searchTerm)"
|
||||
>
|
||||
<i class="icon-search" />
|
||||
</button>
|
||||
<i
|
||||
class="button-icon icon-cancel user-finder-icon"
|
||||
class="button-icon icon-cancel"
|
||||
@click.prevent.stop="toggleHidden"
|
||||
/>
|
||||
</template>
|
||||
|
@ -38,22 +38,24 @@
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<script src="./user_finder.js"></script>
|
||||
<script src="./search_bar.js"></script>
|
||||
|
||||
<style lang="scss">
|
||||
@import '../../_variables.scss';
|
||||
|
||||
.user-finder-container {
|
||||
.search-bar-container {
|
||||
max-width: 100%;
|
||||
display: inline-flex;
|
||||
align-items: baseline;
|
||||
vertical-align: baseline;
|
||||
justify-content: flex-end;
|
||||
|
||||
.user-finder-input,
|
||||
.search-bar-input,
|
||||
.search-button {
|
||||
height: 29px;
|
||||
}
|
||||
.user-finder-input {
|
||||
|
||||
.search-bar-input {
|
||||
// TODO: do this properly without a rough guesstimate of 2 icons + paddings
|
||||
max-width: calc(100% - 30px - 30px - 20px);
|
||||
}
|
||||
|
@ -62,6 +64,10 @@
|
|||
margin-left: .5em;
|
||||
margin-right: .5em;
|
||||
}
|
||||
|
||||
.icon-cancel {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
|
@ -100,8 +100,8 @@
|
|||
</ul>
|
||||
<ul>
|
||||
<li @click="toggleDrawer">
|
||||
<router-link :to="{ name: 'user-search' }">
|
||||
{{ $t("nav.user_search") }}
|
||||
<router-link :to="{ name: 'search' }">
|
||||
{{ $t("nav.search") }}
|
||||
</router-link>
|
||||
</li>
|
||||
<li
|
||||
|
|
|
@ -173,12 +173,13 @@ const Status = {
|
|||
if (this.status.type === 'retweet') {
|
||||
return false
|
||||
}
|
||||
var checkFollowing = this.$store.state.config.replyVisibility === 'following'
|
||||
const checkFollowing = this.$store.state.config.replyVisibility === 'following'
|
||||
for (var i = 0; i < this.status.attentions.length; ++i) {
|
||||
if (this.status.user.id === this.status.attentions[i].id) {
|
||||
continue
|
||||
}
|
||||
if (checkFollowing && this.$store.getters.findUser(this.status.attentions[i].id).following) {
|
||||
const taggedUser = this.$store.getters.findUser(this.status.attentions[i].id)
|
||||
if (checkFollowing && taggedUser && taggedUser.following) {
|
||||
return false
|
||||
}
|
||||
if (this.status.attentions[i].id === this.$store.state.users.currentUser.id) {
|
||||
|
@ -418,6 +419,18 @@ const Status = {
|
|||
window.scrollBy(0, rect.bottom - window.innerHeight + 50)
|
||||
}
|
||||
}
|
||||
},
|
||||
'status.repeat_num': function (num) {
|
||||
// refetch repeats when repeat_num is changed in any way
|
||||
if (this.isFocused && this.statusFromGlobalRepository.rebloggedBy && this.statusFromGlobalRepository.rebloggedBy.length !== num) {
|
||||
this.$store.dispatch('fetchRepeats', this.status.id)
|
||||
}
|
||||
},
|
||||
'status.fave_num': function (num) {
|
||||
// refetch favs when fave_num is changed in any way
|
||||
if (this.isFocused && this.statusFromGlobalRepository.favoritedBy && this.statusFromGlobalRepository.favoritedBy.length !== num) {
|
||||
this.$store.dispatch('fetchFavs', this.status.id)
|
||||
}
|
||||
}
|
||||
},
|
||||
filters: {
|
||||
|
|
|
@ -4,7 +4,7 @@ import './tab_switcher.scss'
|
|||
|
||||
export default Vue.component('tab-switcher', {
|
||||
name: 'TabSwitcher',
|
||||
props: ['renderOnlyFocused', 'onSwitch'],
|
||||
props: ['renderOnlyFocused', 'onSwitch', 'customActive'],
|
||||
data () {
|
||||
return {
|
||||
active: this.$slots.default.findIndex(_ => _.tag)
|
||||
|
@ -24,6 +24,14 @@ export default Vue.component('tab-switcher', {
|
|||
}
|
||||
this.active = index
|
||||
}
|
||||
},
|
||||
isActiveTab (index) {
|
||||
const customActiveIndex = this.$slots.default.findIndex(slot => {
|
||||
const dataFilter = slot.data && slot.data.attrs && slot.data.attrs['data-filter']
|
||||
return this.customActive && this.customActive === dataFilter
|
||||
})
|
||||
|
||||
return customActiveIndex > -1 ? customActiveIndex === index : index === this.active
|
||||
}
|
||||
},
|
||||
render (h) {
|
||||
|
@ -33,7 +41,7 @@ export default Vue.component('tab-switcher', {
|
|||
const classesTab = ['tab']
|
||||
const classesWrapper = ['tab-wrapper']
|
||||
|
||||
if (index === this.active) {
|
||||
if (this.isActiveTab(index)) {
|
||||
classesTab.push('active')
|
||||
classesWrapper.push('active')
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import UserAvatar from '../user_avatar/user_avatar.vue'
|
||||
import RemoteFollow from '../remote_follow/remote_follow.vue'
|
||||
import ProgressButton from '../progress_button/progress_button.vue'
|
||||
import ModerationTools from '../moderation_tools/moderation_tools.vue'
|
||||
import { hex2rgb } from '../../services/color_convert/color_convert.js'
|
||||
import { requestFollow, requestUnfollow } from '../../services/follow_manipulate/follow_manipulate'
|
||||
|
@ -104,7 +105,8 @@ export default {
|
|||
components: {
|
||||
UserAvatar,
|
||||
RemoteFollow,
|
||||
ModerationTools
|
||||
ModerationTools,
|
||||
ProgressButton
|
||||
},
|
||||
methods: {
|
||||
followUser () {
|
||||
|
@ -135,6 +137,12 @@ export default {
|
|||
unmuteUser () {
|
||||
this.$store.dispatch('unmuteUser', this.user.id)
|
||||
},
|
||||
subscribeUser () {
|
||||
return this.$store.dispatch('subscribeUser', this.user.id)
|
||||
},
|
||||
unsubscribeUser () {
|
||||
return this.$store.dispatch('unsubscribeUser', this.user.id)
|
||||
},
|
||||
setProfileView (v) {
|
||||
if (this.switcher) {
|
||||
const store = this.$store
|
||||
|
|
|
@ -112,101 +112,120 @@
|
|||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="isOtherUser"
|
||||
v-if="loggedIn && isOtherUser"
|
||||
class="user-interactions"
|
||||
>
|
||||
<div
|
||||
v-if="loggedIn"
|
||||
class="follow"
|
||||
>
|
||||
<span v-if="user.following">
|
||||
<!--Following them!-->
|
||||
<button
|
||||
class="pressed"
|
||||
:disabled="followRequestInProgress"
|
||||
:title="$t('user_card.follow_unfollow')"
|
||||
@click="unfollowUser"
|
||||
>
|
||||
<template v-if="followRequestInProgress">
|
||||
{{ $t('user_card.follow_progress') }}
|
||||
</template>
|
||||
<template v-else>
|
||||
{{ $t('user_card.following') }}
|
||||
</template>
|
||||
</button>
|
||||
</span>
|
||||
<span v-if="!user.following">
|
||||
<button
|
||||
:disabled="followRequestInProgress"
|
||||
:title="followRequestSent ? $t('user_card.follow_again') : ''"
|
||||
@click="followUser"
|
||||
>
|
||||
<template v-if="followRequestInProgress">
|
||||
{{ $t('user_card.follow_progress') }}
|
||||
</template>
|
||||
<template v-else-if="followRequestSent">
|
||||
{{ $t('user_card.follow_sent') }}
|
||||
</template>
|
||||
<template v-else>
|
||||
{{ $t('user_card.follow') }}
|
||||
</template>
|
||||
</button>
|
||||
</span>
|
||||
<div v-if="!user.following">
|
||||
<button
|
||||
class="btn btn-default btn-block"
|
||||
:disabled="followRequestInProgress"
|
||||
:title="followRequestSent ? $t('user_card.follow_again') : ''"
|
||||
@click="followUser"
|
||||
>
|
||||
<template v-if="followRequestInProgress">
|
||||
{{ $t('user_card.follow_progress') }}
|
||||
</template>
|
||||
<template v-else-if="followRequestSent">
|
||||
{{ $t('user_card.follow_sent') }}
|
||||
</template>
|
||||
<template v-else>
|
||||
{{ $t('user_card.follow') }}
|
||||
</template>
|
||||
</button>
|
||||
</div>
|
||||
<div v-else-if="followRequestInProgress">
|
||||
<button
|
||||
class="btn btn-default btn-block pressed"
|
||||
disabled
|
||||
:title="$t('user_card.follow_unfollow')"
|
||||
@click="unfollowUser"
|
||||
>
|
||||
{{ $t('user_card.follow_progress') }}
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
v-if="isOtherUser && loggedIn"
|
||||
class="mute"
|
||||
v-else
|
||||
class="btn-group"
|
||||
>
|
||||
<span v-if="user.muted">
|
||||
<button
|
||||
class="pressed"
|
||||
@click="unmuteUser"
|
||||
>
|
||||
{{ $t('user_card.muted') }}
|
||||
</button>
|
||||
</span>
|
||||
<span v-if="!user.muted">
|
||||
<button @click="muteUser">
|
||||
{{ $t('user_card.mute') }}
|
||||
</button>
|
||||
</span>
|
||||
<button
|
||||
class="btn btn-default pressed"
|
||||
:title="$t('user_card.follow_unfollow')"
|
||||
@click="unfollowUser"
|
||||
>
|
||||
{{ $t('user_card.following') }}
|
||||
</button>
|
||||
<ProgressButton
|
||||
v-if="!user.subscribed"
|
||||
class="btn btn-default"
|
||||
:click="subscribeUser"
|
||||
:title="$t('user_card.subscribe')"
|
||||
>
|
||||
<i class="icon-bell-alt" />
|
||||
</ProgressButton>
|
||||
<ProgressButton
|
||||
v-else
|
||||
class="btn btn-default pressed"
|
||||
:click="unsubscribeUser"
|
||||
:title="$t('user_card.unsubscribe')"
|
||||
>
|
||||
<i class="icon-bell-ringing-o" />
|
||||
</ProgressButton>
|
||||
</div>
|
||||
<div v-if="!loggedIn && user.is_local">
|
||||
<RemoteFollow :user="user" />
|
||||
|
||||
<div>
|
||||
<button
|
||||
v-if="user.muted"
|
||||
class="btn btn-default btn-block pressed"
|
||||
@click="unmuteUser"
|
||||
>
|
||||
{{ $t('user_card.muted') }}
|
||||
</button>
|
||||
<button
|
||||
v-else
|
||||
class="btn btn-default btn-block"
|
||||
@click="muteUser"
|
||||
>
|
||||
{{ $t('user_card.mute') }}
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
v-if="isOtherUser && loggedIn"
|
||||
class="block"
|
||||
>
|
||||
<span v-if="user.statusnet_blocking">
|
||||
<button
|
||||
class="pressed"
|
||||
@click="unblockUser"
|
||||
>
|
||||
{{ $t('user_card.blocked') }}
|
||||
</button>
|
||||
</span>
|
||||
<span v-if="!user.statusnet_blocking">
|
||||
<button @click="blockUser">
|
||||
{{ $t('user_card.block') }}
|
||||
</button>
|
||||
</span>
|
||||
|
||||
<div>
|
||||
<button
|
||||
v-if="user.statusnet_blocking"
|
||||
class="btn btn-default btn-block pressed"
|
||||
@click="unblockUser"
|
||||
>
|
||||
{{ $t('user_card.blocked') }}
|
||||
</button>
|
||||
<button
|
||||
v-else
|
||||
class="btn btn-default btn-block"
|
||||
@click="blockUser"
|
||||
>
|
||||
{{ $t('user_card.block') }}
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
v-if="isOtherUser && loggedIn"
|
||||
class="block"
|
||||
>
|
||||
<span>
|
||||
<button @click="reportUser">
|
||||
{{ $t('user_card.report') }}
|
||||
</button>
|
||||
</span>
|
||||
|
||||
<div>
|
||||
<button
|
||||
class="btn btn-default btn-block"
|
||||
@click="reportUser"
|
||||
>
|
||||
{{ $t('user_card.report') }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<ModerationTools
|
||||
v-if="loggedIn.role === "admin""
|
||||
:user="user"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
v-if="!loggedIn && user.is_local"
|
||||
class="user-interactions"
|
||||
>
|
||||
<RemoteFollow :user="user" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
|
@ -487,40 +506,22 @@
|
|||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
justify-content: space-between;
|
||||
|
||||
margin-right: -.75em;
|
||||
|
||||
div {
|
||||
> * {
|
||||
flex: 1 0 0;
|
||||
margin-right: .75em;
|
||||
margin-bottom: .6em;
|
||||
margin: 0 .75em .6em 0;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.mute {
|
||||
max-width: 220px;
|
||||
min-height: 28px;
|
||||
}
|
||||
|
||||
.follow {
|
||||
max-width: 220px;
|
||||
min-height: 28px;
|
||||
}
|
||||
|
||||
button {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.remote-button {
|
||||
height: 28px !important;
|
||||
width: 92%;
|
||||
}
|
||||
|
||||
.pressed {
|
||||
border-bottom-color: rgba(255, 255, 255, 0.2);
|
||||
border-top-color: rgba(0, 0, 0, 0.2);
|
||||
&.pressed {
|
||||
// TODO: This should be themed.
|
||||
border-bottom-color: rgba(255, 255, 255, 0.2);
|
||||
border-top-color: rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
const UserFinder = {
|
||||
data: () => ({
|
||||
username: undefined,
|
||||
hidden: true,
|
||||
error: false,
|
||||
loading: false
|
||||
}),
|
||||
methods: {
|
||||
findUser (username) {
|
||||
this.$router.push({ name: 'user-search', query: { query: username } })
|
||||
this.$refs.userSearchInput.focus()
|
||||
},
|
||||
toggleHidden () {
|
||||
this.hidden = !this.hidden
|
||||
this.$emit('toggled', this.hidden)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default UserFinder
|
|
@ -3,7 +3,6 @@ import UserCard from '../user_card/user_card.vue'
|
|||
import FollowCard from '../follow_card/follow_card.vue'
|
||||
import Timeline from '../timeline/timeline.vue'
|
||||
import Conversation from '../conversation/conversation.vue'
|
||||
import ModerationTools from '../moderation_tools/moderation_tools.vue'
|
||||
import List from '../list/list.vue'
|
||||
import withLoadMore from '../../hocs/with_load_more/with_load_more'
|
||||
|
||||
|
@ -132,7 +131,6 @@ const UserProfile = {
|
|||
Timeline,
|
||||
FollowerList,
|
||||
FriendList,
|
||||
ModerationTools,
|
||||
FollowCard,
|
||||
Conversation
|
||||
}
|
||||
|
|
|
@ -1,51 +0,0 @@
|
|||
import FollowCard from '../follow_card/follow_card.vue'
|
||||
import map from 'lodash/map'
|
||||
|
||||
const userSearch = {
|
||||
components: {
|
||||
FollowCard
|
||||
},
|
||||
props: [
|
||||
'query'
|
||||
],
|
||||
data () {
|
||||
return {
|
||||
username: '',
|
||||
userIds: [],
|
||||
loading: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
users () {
|
||||
return this.userIds.map(userId => this.$store.getters.findUser(userId))
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.search(this.query)
|
||||
},
|
||||
watch: {
|
||||
query (newV) {
|
||||
this.search(newV)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
newQuery (query) {
|
||||
this.$router.push({ name: 'user-search', query: { query } })
|
||||
this.$refs.userSearchInput.focus()
|
||||
},
|
||||
search (query) {
|
||||
if (!query) {
|
||||
this.users = []
|
||||
return
|
||||
}
|
||||
this.loading = true
|
||||
this.$store.dispatch('searchUsers', query)
|
||||
.then((res) => {
|
||||
this.loading = false
|
||||
this.userIds = map(res, 'id')
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default userSearch
|
|
@ -1,57 +0,0 @@
|
|||
<template>
|
||||
<div class="user-search panel panel-default">
|
||||
<div class="panel-heading">
|
||||
{{ $t('nav.user_search') }}
|
||||
</div>
|
||||
<div class="user-search-input-container">
|
||||
<input
|
||||
ref="userSearchInput"
|
||||
v-model="username"
|
||||
class="user-finder-input"
|
||||
:placeholder="$t('finder.find_user')"
|
||||
@keyup.enter="newQuery(username)"
|
||||
>
|
||||
<button
|
||||
class="btn search-button"
|
||||
@click="newQuery(username)"
|
||||
>
|
||||
<i class="icon-search" />
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
v-if="loading"
|
||||
class="text-center loading-icon"
|
||||
>
|
||||
<i class="icon-spin3 animate-spin" />
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
class="panel-body"
|
||||
>
|
||||
<FollowCard
|
||||
v-for="user in users"
|
||||
:key="user.id"
|
||||
:user="user"
|
||||
class="list-item"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script src="./user_search.js"></script>
|
||||
|
||||
<style lang="scss">
|
||||
.user-search-input-container {
|
||||
margin: 0.5em;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
.search-button {
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
}
|
||||
|
||||
.loading-icon {
|
||||
padding: 1em;
|
||||
}
|
||||
</style>
|
|
@ -17,7 +17,6 @@ import Autosuggest from '../autosuggest/autosuggest.vue'
|
|||
import Importer from '../importer/importer.vue'
|
||||
import Exporter from '../exporter/exporter.vue'
|
||||
import withSubscription from '../../hocs/with_subscription/with_subscription'
|
||||
import userSearchApi from '../../services/new_api/user_search.js'
|
||||
import Mfa from './mfa.vue'
|
||||
|
||||
const BlockList = withSubscription({
|
||||
|
@ -322,11 +321,8 @@ const UserSettings = {
|
|||
})
|
||||
},
|
||||
queryUserIds (query) {
|
||||
return userSearchApi.search({ query, store: this.$store })
|
||||
.then((users) => {
|
||||
this.$store.dispatch('addNewUsers', users)
|
||||
return map(users, 'id')
|
||||
})
|
||||
return this.$store.dispatch('searchUsers', query)
|
||||
.then((users) => map(users, 'id'))
|
||||
},
|
||||
blockUsers (ids) {
|
||||
return this.$store.dispatch('blockUsers', ids)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue