Search history
This commit is contained in:
		
							parent
							
								
									79cfb2b938
								
							
						
					
					
						commit
						a95d483eb6
					
				| @ -15,7 +15,12 @@ | ||||
|                 Searching for <span>{{searchString}}</span> | ||||
|             </div> | ||||
| 
 | ||||
|             <button class="btn btn-outline-secondary" @click.prevent="reset()">Reset</button> | ||||
|             <div class="btn-group"> | ||||
|                 <button class="btn btn-outline-secondary" @click.prevent="showHistory()">History</button> | ||||
|                 <button class="btn btn-outline-secondary" @click.prevent="reset()">Reset</button> | ||||
|             </div> | ||||
| 
 | ||||
|             <search-history ref="searchHistory" :callback="updateSearch" :curSearch="data"></search-history> | ||||
|         </div> | ||||
|         <div v-else-if="results" class="results"> | ||||
|             <h4> | ||||
| @ -44,21 +49,20 @@ | ||||
|     import Modal from '../components/Modal.vue'; | ||||
|     import {characterImage} from './common'; | ||||
|     import core from './core'; | ||||
|     import {Character, Connection} from './interfaces'; | ||||
|     import { Character, Connection, SearchData, SearchKink } from './interfaces'; | ||||
|     import l from './localize'; | ||||
|     import UserView from './UserView.vue'; | ||||
|     import * as _ from 'lodash'; | ||||
|     import {EventBus} from './preview/event-bus'; | ||||
|     import CharacterSearchHistory from './CharacterSearchHistory.vue'; | ||||
| 
 | ||||
|     type Options = { | ||||
|         kinks: Kink[], | ||||
|         kinks: SearchKink[], | ||||
|         listitems: {id: string, name: string, value: string}[] | ||||
|     }; | ||||
| 
 | ||||
|     let options: Options | undefined; | ||||
| 
 | ||||
|     type Kink = {id: number, name: string, description: string}; | ||||
| 
 | ||||
|     function sort(x: Character, y: Character): number { | ||||
|         if(x.status === 'looking' && y.status !== 'looking') return -1; | ||||
|         if(x.status !== 'looking' && y.status === 'looking') return 1; | ||||
| @ -87,18 +91,9 @@ | ||||
|         return 0; | ||||
|     } | ||||
| 
 | ||||
|     interface Data { | ||||
|         kinks: Kink[] | ||||
|         genders: string[] | ||||
|         orientations: string[] | ||||
|         languages: string[] | ||||
|         furryprefs: string[] | ||||
|         roles: string[] | ||||
|         positions: string[] | ||||
|     } | ||||
| 
 | ||||
|     @Component({ | ||||
|         components: {modal: Modal, user: UserView, 'filterable-select': FilterableSelect, bbcode: BBCodeView(core.bbCodeParser)} | ||||
|         components: {modal: Modal, user: UserView, 'filterable-select': FilterableSelect, bbcode: BBCodeView(core.bbCodeParser), 'search-history': CharacterSearchHistory} | ||||
|     }) | ||||
|     export default class CharacterSearch extends CustomDialog { | ||||
|         l = l; | ||||
| @ -107,9 +102,9 @@ | ||||
|         results: Character[] | undefined; | ||||
|         resultsComplete = false; | ||||
|         characterImage = characterImage; | ||||
|         options!: Data; | ||||
|         data: Data = {kinks: [], genders: [], orientations: [], languages: [], furryprefs: [], roles: [], positions: []}; | ||||
|         listItems: ReadonlyArray<keyof Data> = ['genders', 'orientations', 'languages', 'furryprefs', 'roles', 'positions']; | ||||
|         options!: SearchData; | ||||
|         data: SearchData = {kinks: [], genders: [], orientations: [], languages: [], furryprefs: [], roles: [], positions: []}; | ||||
|         listItems: ReadonlyArray<keyof SearchData> = ['genders', 'orientations', 'languages', 'furryprefs', 'roles', 'positions']; | ||||
| 
 | ||||
|         searchString = ''; | ||||
| 
 | ||||
| @ -216,7 +211,7 @@ | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         filterKink(filter: RegExp, kink: Kink): boolean { | ||||
|         filterKink(filter: RegExp, kink: SearchKink): boolean { | ||||
|             if(this.data.kinks.length >= 5) | ||||
|                 return this.data.kinks.indexOf(kink) !== -1; | ||||
|             return filter.test(kink.name); | ||||
| @ -232,6 +227,13 @@ | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         updateSearch(data?: SearchData): void { | ||||
|           if (data) { | ||||
|             this.data = data; | ||||
|           } | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         submit(): void { | ||||
|             if(this.results !== undefined) { | ||||
|                 this.results = undefined; | ||||
| @ -240,11 +242,29 @@ | ||||
|             this.error = ''; | ||||
|             const data: Connection.ClientCommands['FKS'] & {[key: string]: (string | number)[]} = {kinks: []}; | ||||
|             for(const key in this.data) { | ||||
|                 const item = this.data[<keyof Data>key]; | ||||
|                 const item = this.data[<keyof SearchData>key]; | ||||
|                 if(item.length > 0) | ||||
|                     data[key] = key === 'kinks' ? (<Kink[]>item).map((x) => x.id) : (<string[]>item); | ||||
|                     data[key] = key === 'kinks' ? (<SearchKink[]>item).map((x) => x.id) : (<string[]>item); | ||||
|             } | ||||
|             core.connection.send('FKS', data); | ||||
| 
 | ||||
|             // tslint:disable-next-line | ||||
|             this.updateSearchHistory(this.data); | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         showHistory(): void { | ||||
|           (<CharacterSearchHistory>this.$refs.searchHistory).show(); | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         async updateSearchHistory(data: SearchData): Promise<void> { | ||||
|             const history = (await core.settingsStore.get('searchHistory')) || []; | ||||
|             const dataStr = JSON.stringify(data, null, 0); | ||||
|             const filteredHistory = _.reject(history, (h: SearchData) => (JSON.stringify(h, null, 0) === dataStr)); | ||||
|             const newHistory: SearchData[] = _.take(_.concat([data], filteredHistory), 15); | ||||
| 
 | ||||
|             await core.settingsStore.set('searchHistory', newHistory); | ||||
|         } | ||||
|     } | ||||
| </script> | ||||
|  | ||||
							
								
								
									
										130
									
								
								chat/CharacterSearchHistory.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										130
									
								
								chat/CharacterSearchHistory.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,130 @@ | ||||
| <template> | ||||
|     <modal ref="dialog" action="Search history" buttonText="Select" @open="onMounted()" @submit="selectStatus" dialogClass="w-100 modal-lg"> | ||||
|         <form class="search-history" v-if="history.length > 0"> | ||||
|             <div class="form-row" v-for="(search, index) in history" :class="{ 'selected-row': (index === selectedSearch)}"> | ||||
|                 <div class="form-col radio-col"> | ||||
|                     <input type="radio" :id="'search_history_' + index" :name="'search_history_' + index" v-model="selectedSearch" v-bind:value="index" /> | ||||
|                 </div> | ||||
|                 <div class="form-col content-col"> | ||||
|                     <label class="custom-control-label" :for="'search_history_' + index" @dblclick="submit"> | ||||
|                         {{describeSearch(search)}} | ||||
|                     </label> | ||||
|                 </div> | ||||
|             </div> | ||||
|         </form> | ||||
|         <div v-else> | ||||
|             <i>This character has no search history.</i> | ||||
|         </div> | ||||
|     </modal> | ||||
| </template> | ||||
| 
 | ||||
| <script lang="ts"> | ||||
|     import { Component, Hook, Prop } from '@f-list/vue-ts'; | ||||
|     import Modal from '../components/Modal.vue'; | ||||
|     import Dropdown from '../components/Dropdown.vue'; | ||||
|     import CustomDialog from '../components/custom_dialog'; | ||||
|     import core from './core'; | ||||
|     import { BBCodeView } from '../bbcode/view'; | ||||
|     import * as _ from 'lodash'; | ||||
|     import { SearchData } from './interfaces'; | ||||
| 
 | ||||
|     @Component({ | ||||
|         components: {modal: Modal, dropdown: Dropdown, bbcode: BBCodeView(core.bbCodeParser)} | ||||
|     }) | ||||
|     export default class CharacterSearchHistory extends CustomDialog { | ||||
|         @Prop({required: true}) | ||||
|         readonly callback!: (searchData: SearchData) => void; | ||||
| 
 | ||||
|         @Prop({required: true}) | ||||
|         readonly curSearch!: SearchData | undefined; | ||||
| 
 | ||||
|         history: SearchData[] = []; | ||||
| 
 | ||||
|         selectedSearch: number | null = null; | ||||
| 
 | ||||
|         @Hook('mounted') | ||||
|         async onMounted(): Promise<void> { | ||||
|             this.history = (await core.settingsStore.get('searchHistory')) || []; | ||||
|             this.selectedSearch = null; | ||||
| 
 | ||||
|             if (this.curSearch) { | ||||
|                 const cleanedSearch = JSON.stringify(this.curSearch, null, 0); | ||||
| 
 | ||||
|                 const index = _.findIndex( | ||||
|                     this.history, | ||||
|                   (c) => (JSON.stringify(c, null, 0) === cleanedSearch) | ||||
|                 ); | ||||
| 
 | ||||
|                 if (index >= 0) { | ||||
|                     this.selectedSearch = index; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         selectStatus(): void { | ||||
|             if (this.selectedSearch !== null) { | ||||
|                 this.callback(this.history[this.selectedSearch]); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         submit(e: Event): void { | ||||
|           (<Modal>this.$refs.dialog).submit(e); | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         describeSearch(searchData: SearchData): string { | ||||
|             return _.join( | ||||
|                 _.map( | ||||
|                     // tslint:disable-next-line no-unsafe-any no-any | ||||
|                     _.flatten(_.map(searchData as any)), | ||||
|                     // tslint:disable-next-line no-unsafe-any no-any | ||||
|                     (v) => _.get(v, 'name', v) | ||||
|                 ), | ||||
|                 ', ' | ||||
|             ); | ||||
|         } | ||||
|     } | ||||
| </script> | ||||
| 
 | ||||
| <style lang="scss"> | ||||
|     .search-history { | ||||
|         .radio-col { | ||||
|             display: none; | ||||
|         } | ||||
| 
 | ||||
|         label::before { | ||||
|             display:none !important; | ||||
|         } | ||||
| 
 | ||||
|         .content-col { | ||||
|             min-width: 100%; | ||||
| 
 | ||||
|             label { | ||||
|                 min-width: 100%; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         .form-row { | ||||
|             margin-bottom: 10px; | ||||
|             padding: 3px; | ||||
| 
 | ||||
|             border: 1px solid rgba(0,0,0,0); | ||||
|             border-radius: 2px; | ||||
|         } | ||||
| 
 | ||||
|         .form-row:hover { | ||||
|             background-color: #20203e; | ||||
|             border: 1px solid #2d2d6b; | ||||
|             border-radius: 2px; | ||||
|         } | ||||
| 
 | ||||
|         .selected-row, | ||||
|         .form-row.selected-row:hover { | ||||
|             background-color: #343461; | ||||
|             border: 1px solid #6565b2; | ||||
|             border-radius: 2px; | ||||
|         } | ||||
|     } | ||||
| </style> | ||||
| @ -186,3 +186,9 @@ import {InlineDisplayMode} from '../interfaces'; | ||||
|         } | ||||
|     } | ||||
| </script> | ||||
| 
 | ||||
| <style lang="scss"> | ||||
|     .modal-footer { | ||||
|         min-height: initial; | ||||
|     } | ||||
| </style> | ||||
|  | ||||
| @ -1,12 +1,12 @@ | ||||
| <template> | ||||
|     <modal action="Status message history" buttonText="Select" @open="onMounted()" @submit="selectStatus" dialogClass="w-100 modal-lg"> | ||||
|     <modal ref="dialog" action="Status message history" buttonText="Select" @open="onMounted()" @submit="selectStatus" dialogClass="w-100 modal-lg"> | ||||
|         <form class="status-picker" v-if="history.length > 0"> | ||||
|             <div class="form-row" v-for="(historicStatus, index) in history" :class="{ 'selected-row': (index === selectedStatus)}"> | ||||
|                 <div class="form-col radio-col"> | ||||
|                     <input type="radio" :id="'history_status_' + index" :name="'history_status_' + index" v-model="selectedStatus" v-bind:value="index" /> | ||||
|                 </div> | ||||
|                 <div class="form-col content-col"> | ||||
|                     <label class="custom-control-label" :for="'history_status_' + index"> | ||||
|                     <label class="custom-control-label" :for="'history_status_' + index" @dblclick="submit"> | ||||
|                         <bbcode :text="historicStatus"></bbcode> | ||||
|                     </label> | ||||
|                 </div> | ||||
| @ -61,6 +61,11 @@ | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         submit(e: Event): void { | ||||
|           (<Modal>this.$refs.dialog).submit(e); | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         selectStatus(): void { | ||||
|             if (this.selectedStatus !== null) { | ||||
|                 this.callback(this.history[this.selectedStatus]); | ||||
|  | ||||
| @ -143,6 +143,19 @@ export interface Logs { | ||||
|     canZip: boolean; | ||||
| } | ||||
| 
 | ||||
| export type SearchKink = {id: number, name: string, description: string}; | ||||
| 
 | ||||
| export interface SearchData { | ||||
|     kinks: SearchKink[] | ||||
|     genders: string[] | ||||
|     orientations: string[] | ||||
|     languages: string[] | ||||
|     furryprefs: string[] | ||||
|     roles: string[] | ||||
|     positions: string[] | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| export namespace Settings { | ||||
|     export type Keys = { | ||||
|         settings: Settings | ||||
| @ -153,6 +166,7 @@ export namespace Settings { | ||||
|         recentChannels: Conversation.RecentChannelConversation[] | ||||
|         hiddenUsers: string[] | ||||
|         statusHistory: string[] | ||||
|         searchHistory: SearchData[] | ||||
|     }; | ||||
| 
 | ||||
|     export interface Store { | ||||
|  | ||||
| @ -126,7 +126,8 @@ export class ImagePreviewMutator { | ||||
|         this.add('youtube.com', this.getBaseJsMutatorScript(['video']), undefined, 'dom-ready'); | ||||
|         this.add('instantfap.com', this.getBaseJsMutatorScript(['#post video', '#post img'])); | ||||
|         this.add('webmshare.com', this.getBaseJsMutatorScript(['video'])); | ||||
|         this.add('pornhub.com', this.getBaseJsMutatorScript(['.mainPlayerDiv video[preload="auto"]', '.mainPlayerDiv video[preload="none"]', '.mainPlayerDiv video', '.photoImageSection img'])); | ||||
|         this.add('pornhub.com', this.getBaseJsMutatorScript(['#video, video', '.mainPlayerDiv video', '.photoImageSection img'])); | ||||
|         this.add('vimeo.com', this.getBaseJsMutatorScript(['#video, video', '#image, img'])); | ||||
|         this.add('sex.com', this.getBaseJsMutatorScript(['.image_frame video', '.image_frame img'])); | ||||
|         this.add('redirect.media.tumblr.com', this.getBaseJsMutatorScript(['picture video', 'picture img'])); | ||||
|         this.add('postimg.cc', this.getBaseJsMutatorScript(['video', '#main-image'])); | ||||
| @ -199,7 +200,7 @@ export class ImagePreviewMutator { | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     getBaseJsMutatorScript(elSelector: string[], skipElementRemove: boolean = false): string { | ||||
|     getBaseJsMutatorScript(elSelector: string[], skipElementRemove: boolean = false, safeTags: string[] = []): string { | ||||
|         return `const { ipcRenderer } = require('electron');
 | ||||
|             const body = document.querySelector('body'); | ||||
|             const html = document.querySelector('html'); | ||||
| @ -252,12 +253,14 @@ export class ImagePreviewMutator { | ||||
|             body.style = 'border: 0 !important; padding: 0 !important; margin: 0 !important; overflow: hidden !important;' | ||||
|                 + 'width: 100% !important; height: 100% !important; opacity: 1 !important;' | ||||
|                 + 'top: 0 !important; left: 0 !important; position: absolute !important;' | ||||
|                 + 'min-width: unset !important; min-height: unset !important; max-width: unset !important; max-height: unset !important;'; | ||||
|                 + 'min-width: unset !important; min-height: unset !important; max-width: unset !important; max-height: unset !important;' | ||||
|                 + 'display: block !important; visibility: visible !important'; | ||||
| 
 | ||||
|             img.style = 'object-position: top left !important; object-fit: contain !important;' | ||||
|                 + 'width: 100% !important; height: 100% !important; opacity: 1 !important;' | ||||
|                 + 'margin: 0 !imporant; border: 0 !important; padding: 0 !important;' | ||||
|                 + 'min-width: unset !important; min-height: unset !important; max-width: unset !important; max-height: unset !important;'; | ||||
|                 + 'min-width: unset !important; min-height: unset !important; max-width: unset !important; max-height: unset !important;' | ||||
|                 + 'display: block !important; visibility: visible !important'; | ||||
| 
 | ||||
|             img.class = ''; | ||||
|             el.class = ''; | ||||
| @ -266,7 +269,8 @@ export class ImagePreviewMutator { | ||||
|             html.style = 'border: 0 !important; padding: 0 !important; margin: 0 !important; overflow: hidden !important;' | ||||
|                 + 'width: 100% !important; height: 100% !important; opacity: 1 !important;' | ||||
|                 + 'top: 0 !important; left: 0 !important; position: absolute !important;' | ||||
|                 + 'min-width: unset !important; min-height: unset !important; max-width: unset !important; max-height: unset !important;'; | ||||
|                 + 'min-width: unset !important; min-height: unset !important; max-width: unset !important; max-height: unset !important;' | ||||
|                 + 'display: block !important; visibility: visible !important'; | ||||
| 
 | ||||
|             ${this.debug ? "console.log('Wrapper', el);" : ''} | ||||
| 
 | ||||
| @ -322,8 +326,14 @@ export class ImagePreviewMutator { | ||||
| 
 | ||||
|             let removeList = []; | ||||
|             const safeIds = ['flistWrapper', 'flistError', 'flistHider']; | ||||
|             const safeTags = [${_.map(safeTags, (t) => `'${t.toLowerCase()}'`).join(',')}]; | ||||
| 
 | ||||
|             body.childNodes.forEach((el) => ((safeIds.indexOf(el.id) < 0) ? removeList.push(el) : true)); | ||||
|             body.childNodes.forEach((el) => ( | ||||
|                 ( | ||||
|                     (safeIds.indexOf(el.id) < 0) | ||||
|                     && ((!el.tagName) || (safeTags.indexOf(el.tagName.toLowerCase())) < 0) | ||||
|                 ) ? removeList.push(el) : true) | ||||
|             ); | ||||
| 
 | ||||
|             ${skipElementRemove ? '' : 'removeList.forEach((el) => el.remove());'} | ||||
|             removeList = []; | ||||
|  | ||||
| @ -41,4 +41,7 @@ | ||||
| 
 | ||||
|     [url=https://www.pornhub.com/view_video.php?viewkey=ph5b2c03dc1e23b]Test[/url] | ||||
| 
 | ||||
|     [url=https://vimeo.com/265884960]Test[/url] | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|  | ||||
| @ -43,6 +43,7 @@ This repository contains a heavily customized version of the mainline F-Chat 3.0 | ||||
|     *    Display match score in search results | ||||
|     *    Current search filters are listed in the search dialog | ||||
|     *    Search filters can be reset | ||||
|     *    Last 15 searches are stored and can be accessed from the 'Search' dialog | ||||
| *   Character status | ||||
|     *    Last 10 status messages are stored and can be accessed from the 'Set status' dialog | ||||
| *   General | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user