diff --git a/src/components/search/search.js b/src/components/search/search.js
index 61a4dae2..db07f5e7 100644
--- a/src/components/search/search.js
+++ b/src/components/search/search.js
@@ -15,6 +15,8 @@ library.add(
faSearch
)
+const allSearchTypes = Object.freeze(['statuses', 'media', 'accounts', 'hashtags'])
+
const Search = {
components: {
FollowCard,
@@ -28,14 +30,17 @@ const Search = {
data () {
return {
loadedInitially: false,
- loading: false,
+ loading: Object.fromEntries(
+ allSearchTypes.map((searchType) => [searchType, false])
+ ),
searchTerm: this.query || '',
userIds: [],
statuses: [],
media: [],
hashtags: [],
+ allTabs: allSearchTypes,
currentResultTab: 'statuses',
- preferredTab : 'statuses',
+ hasUserSelectedTab: false,
statusesOffset: 0,
lastStatusFetchCount: 0,
@@ -72,6 +77,10 @@ const Search = {
canSearchFollowing () {
return this.isLoggedIn &&
this.$store.state.instance.searchOptionFollowingEnabled === true
+ },
+ hasAtLeastOneResult () {
+ return allSearchTypes
+ .some((searchType) => this.getVisibleLength(searchType) > 0)
}
},
mounted () {
@@ -97,12 +106,13 @@ const Search = {
},
async search (query, searchType = null) {
if (!query) {
- this.loading = false
+ for (const searchType of allSearchTypes) {
+ this.loading[searchType] = false
+ }
return
}
const isNewSearch = this.lastQuery !== query
- this.loading = true
this.$refs.searchInput.blur()
if (isNewSearch) {
this.userIds = []
@@ -116,92 +126,102 @@ const Search = {
this.lastMediaFetchCount = 0
}
- let searchTypes = ['statuses', 'media', 'accounts', 'hashtags']
-
+ let searchTypes = allSearchTypes
if (searchType) {
searchTypes = [searchType]
- } else if (this.preferredTab !== 'statuses') {
+ } else if (this.currentResultTab !== 'statuses') {
+ // Sort search order; selected tab first
searchTypes = [
- this.preferredTab,
- ...searchTypes.filter((tab) => tab !== this.preferredTab)
+ this.currentResultTab,
+ ...allSearchTypes.filter((tab) => tab !== this.currentResultTab)
]
}
+ for (const searchType of searchTypes) {
+ this.loading[searchType] = true
+ }
+
let oldStatusesLength = this.statuses.length
let oldMediaLength = this.media.length
let skipMediaSearch = !this.canSearchMediaPosts
for (const searchType of searchTypes) {
- if (searchType === 'media' && skipMediaSearch) {
- continue
- }
-
- let searchOffset
- if (searchType === 'statuses') {
- searchOffset = this.statusesOffset
- } else if (searchType === 'media') {
- searchOffset = this.mediaOffset
- }
-
- const data = await this.$store.dispatch('search', {
- q: query,
- resolve: true,
- offset: searchOffset,
- 'type': searchType,
- following:
- 'followingOnly' in this.filter && this.filter.followingOnly
- })
-
- // Always append to old results. If new results are empty, this doesn't change anything
- this.userIds = this.userIds.concat(map(data.accounts, 'id'))
- this.statuses = uniqBy(this.statuses.concat(data.statuses), 'id')
- if ('media' in data) {
- this.media = uniqBy(this.media.concat(data.media), 'id')
- }
- this.hashtags = this.hashtags.concat(data.hashtags)
-
- if (isNewSearch) {
- this.currentResultTab = this.getActiveTab()
- if (searchType === 'statuses' && data.statuses.length === 0) {
- // safe to assume that there are no media posts
- skipMediaSearch = true
+ try {
+ if (searchType === 'media' && skipMediaSearch) {
+ continue
}
- }
- if (
- !loadedInitially &&
- Object.values(data).some((result) => result.length > 0)
- ) {
- // Show results on the first meaningful response
- this.loadedInitially = true
- }
+ let searchOffset
+ if (searchType === 'statuses') {
+ searchOffset = this.statusesOffset
+ } else if (searchType === 'media') {
+ searchOffset = this.mediaOffset
+ }
- if (searchType === 'statuses') {
- // Offset from whatever we already have
- this.statusesOffset = this.statuses.length
- // Because the amount of new statuses can actually be zero, compare to old length instead
- this.lastStatusFetchCount = this.statuses.length - oldStatusesLength
- } else if (searchType === 'media') {
- this.mediaOffset = this.media.length
- this.lastMediaFetchCount = this.media.length - oldMediaLength
+ const data = await this.$store.dispatch('search', {
+ q: query,
+ resolve: true,
+ offset: searchOffset,
+ 'type': searchType,
+ following:
+ 'followingOnly' in this.filter && this.filter.followingOnly
+ })
+
+ // Always append to old results. If new results are empty, this doesn't change anything
+ this.userIds = this.userIds.concat(map(data.accounts, 'id'))
+ this.statuses = uniqBy(this.statuses.concat(data.statuses), 'id')
+ if ('media' in data) {
+ this.media = uniqBy(this.media.concat(data.media), 'id')
+ }
+ this.hashtags = this.hashtags.concat(data.hashtags)
+
+ if (isNewSearch) {
+ if (!this.hasUserSelectedTab) {
+ this.currentResultTab = this.getFirstTabWithResults()
+ }
+ if (searchType === 'statuses' && data.statuses.length === 0) {
+ // Safe to assume that there are no media posts
+ skipMediaSearch = true
+ }
+ }
+ } catch (error) {
+ console.error(error)
+ } finally {
+ if (!this.loadedInitially && this.hasAtLeastOneResult) {
+ // Show results on the first meaningful response
+ this.loadedInitially = true
+ }
+ this.loading[searchType] = false
+
+ if (searchType === 'statuses') {
+ // Offset from whatever we already have
+ this.statusesOffset = this.statuses.length
+ // Because the amount of new statuses can actually be zero, compare to old length instead
+ this.lastStatusFetchCount = this.statuses.length - oldStatusesLength
+ } else if (searchType === 'media') {
+ this.mediaOffset = this.media.length
+ this.lastMediaFetchCount = this.media.length - oldMediaLength
+ }
}
}
this.lastQuery = query
this.loadedInitially = true
- this.loading = false
+ for (const searchType of allSearchTypes) {
+ this.loading[searchType] = false
+ }
},
- resultCount (tabName) {
- const length = this[tabName].length
+ resultCount (tab) {
+ const length = this.getVisibleLength(tab)
if (length === 0 || !this.loadedInitially) {
return ''
}
if (
- (tabName === 'visibleStatuses' && this.lastStatusFetchCount !== 0) ||
- (tabName === 'visibleMedia' && this.lastMediaFetchCount !== 0)
+ (tab === 'statuses' && this.lastStatusFetchCount !== 0) ||
+ (tab === 'media' && this.lastMediaFetchCount !== 0)
) {
return ` (${length}+)`
}
@@ -210,24 +230,11 @@ const Search = {
},
onResultTabSwitch (key) {
this.currentResultTab = key
- this.preferredTab = key
- this.loading = false
+ this.hasUserSelectedTab = true
},
- getActiveTab () {
- const available = {
- statuses: this.visibleStatuses.length > 0,
- media: this.visibleMedia.length > 0,
- accounts: this.users.length > 0,
- hashtags: this.hashtags.length > 0,
- }
-
- if (available[this.preferredTab]) {
- return this.preferredTab
- }
-
- const tabOrder = ['statuses', 'media', 'accounts', 'hashtags']
- for (const tab of tabOrder) {
- if (available[tab]) {
+ getFirstTabWithResults () {
+ for (const tab of allSearchTypes) {
+ if (this.getVisibleLength(tab) > 0) {
return tab
}
}
@@ -236,6 +243,17 @@ const Search = {
},
lastHistoryRecord (hashtag) {
return hashtag.history && hashtag.history[0]
+ },
+ getVisibleLength (tab) {
+ if (tab === 'statuses') {
+ return this.visibleStatuses.length
+ } else if (tab === 'media') {
+ return this.visibleMedia.length
+ } else if (tab === 'accounts') {
+ return this.users.length
+ } else if (tab === 'hashtags') {
+ return this.hashtags.length
+ }
}
}
}
diff --git a/src/components/search/search.vue b/src/components/search/search.vue
index 1a6e2d1b..389f8900 100644
--- a/src/components/search/search.vue
+++ b/src/components/search/search.vue
@@ -27,23 +27,7 @@