Add Search Suggestions and Custom Invidious Instance
This commit is contained in:
		
							parent
							
								
									2b476f4dc6
								
							
						
					
					
						commit
						c8da6fec3d
					
				|  | @ -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": { | ||||
|  |  | |||
|  | @ -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", | ||||
|  |  | |||
|  | @ -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); | ||||
| } | ||||
|  |  | |||
|  | @ -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) | ||||
| 
 | ||||
|  |  | |||
|  | @ -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> | ||||
| 
 | ||||
|  |  | |||
|  | @ -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', | ||||
|  |  | |||
|  | @ -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> | ||||
| 
 | ||||
|  |  | |||
|  | @ -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) | ||||
| 
 | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
|  | @ -14,7 +14,7 @@ | |||
| .channelInformation { | ||||
|   position: absolute; | ||||
|   bottom: 10px; | ||||
|   width: 300px; | ||||
|   width: 350px; | ||||
| } | ||||
| 
 | ||||
| .channelThumbnail { | ||||
|  |  | |||
|  | @ -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': | ||||
|               commit('setInvidiousInstance', result.value) | ||||
|               if (result.value === '') { | ||||
|                 dispatch('updateInvidiousInstance', 'https://invidio.us') | ||||
|               } else { | ||||
|                 commit('setInvidiousInstance', result.value) | ||||
|               } | ||||
|               break | ||||
|             case 'backendFallback': | ||||
|               commit('setBackendFallback', result.value) | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue