Add Search Suggestions and Custom Invidious Instance

This commit is contained in:
Preston 2020-06-01 22:42:29 -04:00
parent 2b476f4dc6
commit c8da6fec3d
12 changed files with 183 additions and 26 deletions

11
package-lock.json generated
View File

@ -12940,6 +12940,11 @@
"resolved": "https://registry.npmjs.org/lodash.bind/-/lodash.bind-4.2.1.tgz",
"integrity": "sha1-euMBfpOWIqwxt9fX3LGzTbFpDTU="
},
"lodash.debounce": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
"integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168="
},
"lodash.defaults": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
@ -16268,8 +16273,7 @@
},
"minimist": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
"resolved": "",
"dev": true
},
"schema-utils": {
@ -18870,8 +18874,7 @@
},
"minimist": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
"resolved": "",
"dev": true
},
"string-width": {

View File

@ -17,6 +17,7 @@
"dateformat": "^3.0.3",
"electron-context-menu": "^2.0.1",
"jquery": "^3.5.1",
"lodash.debounce": "^4.0.8",
"lodash.isequal": "^4.5.0",
"material-design-icons": "^3.0.1",
"mediaelement": "^4.2.16",

View File

@ -22,27 +22,35 @@
color: var(--tertiary-text-color);
}
.search .ft-input {
.forceTextColor .ft-input {
color: var(--text-with-main-color);
border-bottom: 1px solid var(--text-with-main-color);
}
.search ::-webkit-input-placeholder {
.forceTextColor ::-webkit-input-placeholder {
color: var(--text-with-main-color);
}
.inputAction {
position: absolute;
padding: 10px;
top: 10px;
top: 5px;
right: 0px;
cursor: pointer;
border-radius: 200px 200px 200px 200px;
color: var(--primary-text-color);
}
.search ::-webkit-calendar-picker-indicator {
display: none;
}
.search .inputAction {
color: var(--text-with-main-color)
top: 12px;
}
.forceTextColor .inputAction {
color: var(--text-with-main-color);
}
.inputAction:hover {
@ -52,7 +60,7 @@
transition: background 0.2s ease-in;
}
.search .inputAction:hover {
.forceTextColor .inputAction:hover {
background-color: var(--primary-color-hover);
}
@ -63,6 +71,6 @@
transition: background 0.2s ease-in;
}
.search .inputAction:active {
.forceTextColor .inputAction:active {
background-color: var(--primary-color-active);
}

View File

@ -7,14 +7,26 @@ export default Vue.extend({
type: String,
required: true
},
value: {
type: String,
default: ''
},
showArrow: {
type: Boolean,
default: true
},
showLabel: {
type: Boolean,
default: false
},
isSearch: {
type: Boolean,
default: false
}
},
dataList: {
type: Array,
default: () => { return [] }
},
},
data: function () {
return {
@ -29,6 +41,10 @@ export default Vue.extend({
forceTextColor: function () {
return this.isSearch && this.barColor
},
idDataList: function () {
return `${this.id}_datalist`
}
},
mounted: function () {
@ -41,6 +57,11 @@ export default Vue.extend({
this.$emit('click', this.inputData)
},
handleInput: function (input) {
this.inputData = input
this.$emit('input', input)
},
addListener: function () {
const inputElement = document.getElementById(this.id)

View File

@ -1,14 +1,25 @@
<template>
<div
class="ft-input-component"
:class="{ search: forceTextColor }"
:class="{
search: isSearch,
forceTextColor: forceTextColor
}"
>
<label
v-if="showLabel"
:for="id"
>
{{ placeholder }}
</label>
<input
:id="id"
:list="idDataList"
:value="value"
class="ft-input"
type="text"
:placeholder="placeholder"
@input="e => inputData = e.target.value"
@input="e => handleInput(e.target.value)"
>
<font-awesome-icon
v-if="showArrow"
@ -16,6 +27,16 @@
class="inputAction"
@click="handleClick"
/>
<datalist
v-if="dataList.length > 0"
:id="idDataList"
>
<option
v-for="(list, index) in dataList"
:key="index"
:value="list"
/>
</datalist>
</div>
</template>

View File

@ -3,14 +3,18 @@ import $ from 'jquery'
import { mapActions } from 'vuex'
import FtCard from '../ft-card/ft-card.vue'
import FtSelect from '../ft-select/ft-select.vue'
import FtInput from '../ft-input/ft-input.vue'
import FtToggleSwitch from '../ft-toggle-switch/ft-toggle-switch.vue'
import FtFlexBox from '../ft-flex-box/ft-flex-box.vue'
import debounce from 'lodash.debounce'
export default Vue.extend({
name: 'GeneralSettings',
components: {
'ft-card': FtCard,
'ft-select': FtSelect,
'ft-input': FtInput,
'ft-toggle-switch': FtToggleSwitch,
'ft-flex-box': FtFlexBox
},
@ -597,8 +601,20 @@ export default Vue.extend({
console.log(requestUrl)
console.log(error)
})
this.updateInvidiousInstanceBounce = debounce(this.updateInvidiousInstance, 500)
},
beforeDestroy: function () {
if (this.invidiousInstance === '') {
this.updateInvidiousInstance('https://invidio.us')
}
},
methods: {
handleInvidiousInstanceInput: function (input) {
const invidiousInstance = input.replace(/\/$/, '')
this.updateInvidiousInstanceBounce(invidiousInstance)
},
...mapActions([
'updateBackendFallback',
'updateCheckForUpdates',

View File

@ -57,15 +57,17 @@
:select-values="thumbnailTypeValues"
@change="updateThumbnailPreference"
/>
<ft-select
v-if="showInvidiousInstances"
placeholder="Invidious Instance"
:value="invidiousInstance"
:select-names="instanceNames"
:select-values="instanceValues"
@change="updateInvidiousInstance"
/>
</div>
<ft-flex-box class="generalSettingsFlexBox">
<ft-input
placeholder="Invidious Instance (Default is https://invidio.us)"
:show-arrow="false"
:show-label="true"
:value="invidiousInstance"
:data-list="instanceValues"
@input="handleInvidiousInstanceInput"
/>
</ft-flex-box>
</ft-card>
</template>

View File

@ -3,6 +3,8 @@ import FtInput from '../ft-input/ft-input.vue'
import FtSearchFilters from '../ft-search-filters/ft-search-filters.vue'
import $ from 'jquery'
import router from '../../router/index.js'
import debounce from 'lodash.debounce'
import ytSuggest from 'youtube-suggest'
export default Vue.extend({
name: 'TopNav',
@ -14,7 +16,9 @@ export default Vue.extend({
return {
component: this,
windowWidth: 0,
showFilters: false
showFilters: false,
searchValue: '',
searchSuggestionsDataList: []
}
},
computed: {
@ -28,7 +32,19 @@ export default Vue.extend({
barColor: function () {
return this.$store.getters.getBarColor
}
},
invidiousInstance: function () {
return this.$store.getters.getInvidiousInstance
},
backendFallback: function () {
return this.$store.getters.getBackendFallback
},
backendPreference: function () {
return this.$store.getters.getBackendPreference
},
},
mounted: function () {
const appWidth = $(window).width()
@ -48,6 +64,8 @@ export default Vue.extend({
searchContainer.style.display = 'none'
}
})
this.debounceSearchResults = debounce(this.getSearchSuggestions, 500)
},
methods: {
goToSearch: function (query) {
@ -84,6 +102,55 @@ export default Vue.extend({
this.showFilters = false
},
getSearchSuggestionsDebounce: function (query) {
this.debounceSearchResults(query)
},
getSearchSuggestions: function (query) {
switch (this.backendPreference) {
case 'local':
this.getSearchSuggestionsLocal(query)
break
case 'invidious':
this.getSearchSuggestionsInvidious(query)
break
}
},
getSearchSuggestionsLocal: function (query) {
if (query === '') {
this.searchSuggestionsDataList = []
this.searchValue = ''
return
}
ytSuggest(query).then((results) => {
this.searchSuggestionsDataList = results
this.searchValue = query
})
},
getSearchSuggestionsInvidious: function (query) {
if (query === '') {
this.searchSuggestionsDataList = []
this.searchValue = ''
return
}
const searchPayload = {
resource: 'search/suggestions',
id: '',
params: {
q: query
}
}
this.$store.dispatch('invidiousAPICall', searchPayload).then((results) => {
this.searchSuggestionsDataList = results.suggestions
this.searchValue = query
})
},
toggleSearchContainer: function () {
const searchContainer = $('.searchContainer').get(0)

View File

@ -39,6 +39,9 @@
placeholder="Search / Go to URL"
class="searchInput"
:is-search="true"
:data-list="searchSuggestionsDataList"
:value="searchValue"
@input="getSearchSuggestionsDebounce"
@click="goToSearch"
/>
<font-awesome-icon

View File

@ -14,7 +14,7 @@
.channelInformation {
position: absolute;
bottom: 10px;
width: 300px;
width: 350px;
}
.channelThumbnail {

View File

@ -150,14 +150,18 @@ const getters = {
}
const actions = {
grabUserSettings ({ commit }) {
grabUserSettings ({ dispatch, commit }) {
settingsDb.find({}, (err, results) => {
if (!err) {
console.log(results)
results.forEach((result) => {
switch (result._id) {
case 'invidiousInstance':
if (result.value === '') {
dispatch('updateInvidiousInstance', 'https://invidio.us')
} else {
commit('setInvidiousInstance', result.value)
}
break
case 'backendFallback':
commit('setBackendFallback', result.value)

View File

@ -137,6 +137,7 @@ export default Vue.extend({
this.videoId = this.$route.params.id
this.firstLoad = true
this.activeFormat = this.defaultVideoFormat
this.checkIfPlaylist()
@ -206,6 +207,16 @@ export default Vue.extend({
this.videoDislikeCount = result.dislikes
this.isLive = result.player_response.videoDetails.isLive
const subCount = result.author.subscriber_count
if (subCount >= 1000000) {
this.channelSubscriptionCountText = `${subCount / 1000000}M`
} else if (subCount >= 10000) {
this.channelSubscriptionCountText = `${subCount / 1000}K`
} else {
this.channelSubscriptionCountText = subCount.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',')
}
if (this.isLive) {
this.showLegacyPlayer = true
this.showDashPlayer = false