Skip to content

Commit

Permalink
Implement search history entries showing up alongside YT search sugge…
Browse files Browse the repository at this point in the history
…stions as queries are being typed
  • Loading branch information
kommunarr committed Dec 28, 2024
1 parent a69cbbf commit 24de045
Show file tree
Hide file tree
Showing 6 changed files with 84 additions and 21 deletions.
4 changes: 4 additions & 0 deletions src/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,9 @@ const PLAYLIST_HEIGHT_FORCE_LIST_THRESHOLD = 500
// YouTube search character limit is 100 characters
const SEARCH_CHAR_LIMIT = 100

// matches # of results we show for search suggestions
const SEARCH_RESULTS_DISPLAY_LIMIT = 14

// Displayed on the about page and used in the main.js file to only allow bitcoin URLs with this wallet address to be opened
const ABOUT_BITCOIN_ADDRESS = '1Lih7Ho5gnxb1CwPD4o59ss78pwo2T91eS'

Expand All @@ -205,5 +208,6 @@ export {
MOBILE_WIDTH_THRESHOLD,
PLAYLIST_HEIGHT_FORCE_LIST_THRESHOLD,
SEARCH_CHAR_LIMIT,
SEARCH_RESULTS_DISPLAY_LIMIT,
ABOUT_BITCOIN_ADDRESS,
}
23 changes: 17 additions & 6 deletions src/renderer/components/ft-input/ft-input.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,13 +63,13 @@ export default defineComponent({
type: Array,
default: () => { return [] }
},
searchResultIcon: {
dataListProperties: {
type: Array,
default: null
default: () => { return [] }
},
canRemoveResults: {
type: Boolean,
default: false
searchResultIconNames: {
type: Array,
default: null
},
showDataWhenEmpty: {
type: Boolean,
Expand Down Expand Up @@ -265,7 +265,7 @@ export default defineComponent({
},

handleRemoveClick: function (index) {
if (!this.canRemoveResults) { return }
if (!this.dataListProperties[index]?.isRemoveable) { return }

// keep focus in input even if removed "Remove" button was clicked
this.$refs.input.focus()
Expand Down Expand Up @@ -328,6 +328,16 @@ export default defineComponent({
}
},

getIconFor: function (index) {
if (this.dataListTypes[index] === 'search-history') {
return 'clock-rotate-left'
} else if (this.dataListTypes[index] === 'search-suggestion') {
return 'magnifying-glass'
}

return null
},

updateSelectedOptionIndex: function (index) {
this.searchState.selectedOption = index
// Allow deselecting suggestion
Expand All @@ -351,6 +361,7 @@ export default defineComponent({

updateVisibleDataList: function () {
// Reset selected option before it's updated
// Block resetting if it was just the "Remove" button that was pressed
if (!this.removalMade || this.searchState.selectedOption >= this.dataList.length) {
this.searchState.selectedOption = -1
this.searchState.keyboardSelectedOptionIndex = -1
Expand Down
6 changes: 3 additions & 3 deletions src/renderer/components/ft-input/ft-input.vue
Original file line number Diff line number Diff line change
Expand Up @@ -85,13 +85,13 @@
@mouseleave="searchState.selectedOption = -1; removeButtonSelectedIndex = -1"
>
<font-awesome-icon
v-if="searchResultIcon"
:icon="searchResultIcon"
v-if="dataListProperties[index]?.iconName"
:icon="['fas', dataListProperties[index].iconName]"
class="searchResultIcon"
/>
<span>{{ entry }}</span>
<a
v-if="canRemoveResults"
v-if="dataListProperties[index]?.isRemoveable"
class="removeButton"
:class="{ removeButtonSelected: removeButtonSelectedIndex === index}"
role="button"
Expand Down
45 changes: 39 additions & 6 deletions src/renderer/components/top-nav/top-nav.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import FtProfileSelector from '../ft-profile-selector/ft-profile-selector.vue'
import FtIconButton from '../ft-icon-button/ft-icon-button.vue'
import debounce from 'lodash.debounce'

import { IpcChannels, KeyboardShortcuts, MOBILE_WIDTH_THRESHOLD } from '../../../constants'
import { IpcChannels, KeyboardShortcuts, MOBILE_WIDTH_THRESHOLD, SEARCH_RESULTS_DISPLAY_LIMIT } from '../../../constants'
import { localizeAndAddKeyboardShortcutToActionTitle, openInternalPath } from '../../helpers/utils'
import { translateWindowTitle } from '../../helpers/strings'
import { clearLocalSearchSuggestionsSession, getLocalSearchSuggestions } from '../../helpers/api/local'
Expand Down Expand Up @@ -121,20 +121,53 @@ export default defineComponent({
)
},

usingSearchHistoryResults: function () {
usingOnlySearchHistoryResults: function () {
return this.lastSuggestionQuery === ''
},

latestMatchingSearchHistoryNames: function () {
return this.$store.getters.getLatestMatchingSearchHistoryNames(this.lastSuggestionQuery)
},

latestSearchHistoryNames: function () {
return this.$store.getters.getLatestSearchHistoryNames
},

// show latest search history when the search bar is empty
activeDataList: function () {
if (!this.enableSearchSuggestions) {
return
return []
}

if (this.usingOnlySearchHistoryResults) {
return this.$store.getters.getLatestSearchHistoryNames
}

const searchResults = [...this.latestMatchingSearchHistoryNames]
for (const searchSuggestion of this.searchSuggestionsDataList) {
// prevent duplicate results
if (this.latestMatchingSearchHistoryNames.includes(searchSuggestion)) {
continue
}

searchResults.push(searchSuggestion)

if (searchResults.length === SEARCH_RESULTS_DISPLAY_LIMIT) {
break
}
}
return this.usingSearchHistoryResults ? this.$store.getters.getLatestUniqueSearchHistoryNames : this.searchSuggestionsDataList

return searchResults
},

searchResultIcon: function () {
return this.usingSearchHistoryResults ? ['fas', 'clock-rotate-left'] : ['fas', 'magnifying-glass']
activeDataListProperties: function () {
const searchHistoryNameLength = this.usingOnlySearchHistoryResults ? this.latestSearchHistoryNames.length : this.latestMatchingSearchHistoryNames.length
return this.activeDataList.map((_, i) => {
const isSearchHistoryEntry = searchHistoryNameLength > i
return isSearchHistoryEntry
? { isRemoveable: true, iconName: 'clock-rotate-left' }
: { isRemoveable: false, iconName: 'magnifying-glass' }
})
},
},
watch: {
Expand Down
3 changes: 1 addition & 2 deletions src/renderer/components/top-nav/top-nav.vue
Original file line number Diff line number Diff line change
Expand Up @@ -93,11 +93,10 @@
class="searchInput"
:is-search="true"
:data-list="activeDataList"
:search-result-icon="searchResultIcon"
:data-list-properties="activeDataListProperties"
:spellcheck="false"
:show-clear-text-button="true"
:show-data-when-empty="true"
:can-remove-results="usingSearchHistoryResults"
@input="getSearchSuggestionsDebounce"
@click="goToSearch"
@clear="() => lastSuggestionQuery = ''"
Expand Down
24 changes: 20 additions & 4 deletions src/renderer/store/modules/search-history.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { SEARCH_RESULTS_DISPLAY_LIMIT } from '../../../constants'
import { DBSearchHistoryHandlers } from '../../../datastores/handlers/index'

// matches # of results we show for search suggestions
const SEARCH_HISTORY_ENTRIES_DISPLAY_LIMIT = 14
// maximum number of search history results to display when mixed with regular YT search suggestions
const MIXED_SEARCH_HISTORY_ENTRIES_DISPLAY_LIMIT = 4

const state = {
searchHistoryEntries: []
Expand All @@ -12,8 +13,23 @@ const getters = {
return state.searchHistoryEntries
},

getLatestUniqueSearchHistoryNames: (state) => {
return state.searchHistoryEntries.slice(0, SEARCH_HISTORY_ENTRIES_DISPLAY_LIMIT).map((entry) => entry.name)
getLatestSearchHistoryNames: (state) => {
return state.searchHistoryEntries.slice(0, SEARCH_RESULTS_DISPLAY_LIMIT).map((entry) => entry.name)
},

getLatestMatchingSearchHistoryNames: (state) => (name) => {
const matches = []
for (const entry of state.searchHistoryEntries) {
if (entry.name.startsWith(name)) {
matches.push(entry.name)
if (matches.length === MIXED_SEARCH_HISTORY_ENTRIES_DISPLAY_LIMIT) {
break
}
}
}

// prioritize more concise matches
return matches.toSorted((a, b) => a.length - b.length)
},

getSearchHistoryEntryWithId: (state) => (id) => {
Expand Down

0 comments on commit 24de045

Please sign in to comment.