Search history
This commit is contained in:
parent
79cfb2b938
commit
a95d483eb6
|
@ -15,7 +15,12 @@
|
||||||
Searching for <span>{{searchString}}</span>
|
Searching for <span>{{searchString}}</span>
|
||||||
</div>
|
</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>
|
||||||
<div v-else-if="results" class="results">
|
<div v-else-if="results" class="results">
|
||||||
<h4>
|
<h4>
|
||||||
|
@ -44,21 +49,20 @@
|
||||||
import Modal from '../components/Modal.vue';
|
import Modal from '../components/Modal.vue';
|
||||||
import {characterImage} from './common';
|
import {characterImage} from './common';
|
||||||
import core from './core';
|
import core from './core';
|
||||||
import {Character, Connection} from './interfaces';
|
import { Character, Connection, SearchData, SearchKink } from './interfaces';
|
||||||
import l from './localize';
|
import l from './localize';
|
||||||
import UserView from './UserView.vue';
|
import UserView from './UserView.vue';
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
import {EventBus} from './preview/event-bus';
|
import {EventBus} from './preview/event-bus';
|
||||||
|
import CharacterSearchHistory from './CharacterSearchHistory.vue';
|
||||||
|
|
||||||
type Options = {
|
type Options = {
|
||||||
kinks: Kink[],
|
kinks: SearchKink[],
|
||||||
listitems: {id: string, name: string, value: string}[]
|
listitems: {id: string, name: string, value: string}[]
|
||||||
};
|
};
|
||||||
|
|
||||||
let options: Options | undefined;
|
let options: Options | undefined;
|
||||||
|
|
||||||
type Kink = {id: number, name: string, description: string};
|
|
||||||
|
|
||||||
function sort(x: Character, y: Character): number {
|
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;
|
||||||
if(x.status !== 'looking' && y.status === 'looking') return 1;
|
if(x.status !== 'looking' && y.status === 'looking') return 1;
|
||||||
|
@ -87,18 +91,9 @@
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Data {
|
|
||||||
kinks: Kink[]
|
|
||||||
genders: string[]
|
|
||||||
orientations: string[]
|
|
||||||
languages: string[]
|
|
||||||
furryprefs: string[]
|
|
||||||
roles: string[]
|
|
||||||
positions: string[]
|
|
||||||
}
|
|
||||||
|
|
||||||
@Component({
|
@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 {
|
export default class CharacterSearch extends CustomDialog {
|
||||||
l = l;
|
l = l;
|
||||||
|
@ -107,9 +102,9 @@
|
||||||
results: Character[] | undefined;
|
results: Character[] | undefined;
|
||||||
resultsComplete = false;
|
resultsComplete = false;
|
||||||
characterImage = characterImage;
|
characterImage = characterImage;
|
||||||
options!: Data;
|
options!: SearchData;
|
||||||
data: Data = {kinks: [], genders: [], orientations: [], languages: [], furryprefs: [], roles: [], positions: []};
|
data: SearchData = {kinks: [], genders: [], orientations: [], languages: [], furryprefs: [], roles: [], positions: []};
|
||||||
listItems: ReadonlyArray<keyof Data> = ['genders', 'orientations', 'languages', 'furryprefs', 'roles', 'positions'];
|
listItems: ReadonlyArray<keyof SearchData> = ['genders', 'orientations', 'languages', 'furryprefs', 'roles', 'positions'];
|
||||||
|
|
||||||
searchString = '';
|
searchString = '';
|
||||||
|
|
||||||
|
@ -216,7 +211,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
filterKink(filter: RegExp, kink: Kink): boolean {
|
filterKink(filter: RegExp, kink: SearchKink): boolean {
|
||||||
if(this.data.kinks.length >= 5)
|
if(this.data.kinks.length >= 5)
|
||||||
return this.data.kinks.indexOf(kink) !== -1;
|
return this.data.kinks.indexOf(kink) !== -1;
|
||||||
return filter.test(kink.name);
|
return filter.test(kink.name);
|
||||||
|
@ -232,6 +227,13 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
updateSearch(data?: SearchData): void {
|
||||||
|
if (data) {
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
submit(): void {
|
submit(): void {
|
||||||
if(this.results !== undefined) {
|
if(this.results !== undefined) {
|
||||||
this.results = undefined;
|
this.results = undefined;
|
||||||
|
@ -240,11 +242,29 @@
|
||||||
this.error = '';
|
this.error = '';
|
||||||
const data: Connection.ClientCommands['FKS'] & {[key: string]: (string | number)[]} = {kinks: []};
|
const data: Connection.ClientCommands['FKS'] & {[key: string]: (string | number)[]} = {kinks: []};
|
||||||
for(const key in this.data) {
|
for(const key in this.data) {
|
||||||
const item = this.data[<keyof Data>key];
|
const item = this.data[<keyof SearchData>key];
|
||||||
if(item.length > 0)
|
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);
|
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>
|
</script>
|
||||||
|
|
|
@ -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>
|
|
@ -185,4 +185,10 @@ import {InlineDisplayMode} from '../interfaces';
|
||||||
core.connection.connect(this.selectedCharacter.name);
|
core.connection.connect(this.selectedCharacter.name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.modal-footer {
|
||||||
|
min-height: initial;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
<template>
|
<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">
|
<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-row" v-for="(historicStatus, index) in history" :class="{ 'selected-row': (index === selectedStatus)}">
|
||||||
<div class="form-col radio-col">
|
<div class="form-col radio-col">
|
||||||
<input type="radio" :id="'history_status_' + index" :name="'history_status_' + index" v-model="selectedStatus" v-bind:value="index" />
|
<input type="radio" :id="'history_status_' + index" :name="'history_status_' + index" v-model="selectedStatus" v-bind:value="index" />
|
||||||
</div>
|
</div>
|
||||||
<div class="form-col content-col">
|
<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>
|
<bbcode :text="historicStatus"></bbcode>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
@ -61,6 +61,11 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
submit(e: Event): void {
|
||||||
|
(<Modal>this.$refs.dialog).submit(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
selectStatus(): void {
|
selectStatus(): void {
|
||||||
if (this.selectedStatus !== null) {
|
if (this.selectedStatus !== null) {
|
||||||
this.callback(this.history[this.selectedStatus]);
|
this.callback(this.history[this.selectedStatus]);
|
||||||
|
|
|
@ -143,6 +143,19 @@ export interface Logs {
|
||||||
canZip: boolean;
|
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 namespace Settings {
|
||||||
export type Keys = {
|
export type Keys = {
|
||||||
settings: Settings
|
settings: Settings
|
||||||
|
@ -153,6 +166,7 @@ export namespace Settings {
|
||||||
recentChannels: Conversation.RecentChannelConversation[]
|
recentChannels: Conversation.RecentChannelConversation[]
|
||||||
hiddenUsers: string[]
|
hiddenUsers: string[]
|
||||||
statusHistory: string[]
|
statusHistory: string[]
|
||||||
|
searchHistory: SearchData[]
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface Store {
|
export interface Store {
|
||||||
|
|
|
@ -126,7 +126,8 @@ export class ImagePreviewMutator {
|
||||||
this.add('youtube.com', this.getBaseJsMutatorScript(['video']), undefined, 'dom-ready');
|
this.add('youtube.com', this.getBaseJsMutatorScript(['video']), undefined, 'dom-ready');
|
||||||
this.add('instantfap.com', this.getBaseJsMutatorScript(['#post video', '#post img']));
|
this.add('instantfap.com', this.getBaseJsMutatorScript(['#post video', '#post img']));
|
||||||
this.add('webmshare.com', this.getBaseJsMutatorScript(['video']));
|
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('sex.com', this.getBaseJsMutatorScript(['.image_frame video', '.image_frame img']));
|
||||||
this.add('redirect.media.tumblr.com', this.getBaseJsMutatorScript(['picture video', 'picture img']));
|
this.add('redirect.media.tumblr.com', this.getBaseJsMutatorScript(['picture video', 'picture img']));
|
||||||
this.add('postimg.cc', this.getBaseJsMutatorScript(['video', '#main-image']));
|
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');
|
return `const { ipcRenderer } = require('electron');
|
||||||
const body = document.querySelector('body');
|
const body = document.querySelector('body');
|
||||||
const html = document.querySelector('html');
|
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;'
|
body.style = 'border: 0 !important; padding: 0 !important; margin: 0 !important; overflow: hidden !important;'
|
||||||
+ 'width: 100% !important; height: 100% !important; opacity: 1 !important;'
|
+ 'width: 100% !important; height: 100% !important; opacity: 1 !important;'
|
||||||
+ 'top: 0 !important; left: 0 !important; position: absolute !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;'
|
img.style = 'object-position: top left !important; object-fit: contain !important;'
|
||||||
+ 'width: 100% !important; height: 100% !important; opacity: 1 !important;'
|
+ 'width: 100% !important; height: 100% !important; opacity: 1 !important;'
|
||||||
+ 'margin: 0 !imporant; border: 0 !important; padding: 0 !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 = '';
|
img.class = '';
|
||||||
el.class = '';
|
el.class = '';
|
||||||
|
@ -266,7 +269,8 @@ export class ImagePreviewMutator {
|
||||||
html.style = 'border: 0 !important; padding: 0 !important; margin: 0 !important; overflow: hidden !important;'
|
html.style = 'border: 0 !important; padding: 0 !important; margin: 0 !important; overflow: hidden !important;'
|
||||||
+ 'width: 100% !important; height: 100% !important; opacity: 1 !important;'
|
+ 'width: 100% !important; height: 100% !important; opacity: 1 !important;'
|
||||||
+ 'top: 0 !important; left: 0 !important; position: absolute !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);" : ''}
|
${this.debug ? "console.log('Wrapper', el);" : ''}
|
||||||
|
|
||||||
|
@ -322,8 +326,14 @@ export class ImagePreviewMutator {
|
||||||
|
|
||||||
let removeList = [];
|
let removeList = [];
|
||||||
const safeIds = ['flistWrapper', 'flistError', 'flistHider'];
|
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());'}
|
${skipElementRemove ? '' : 'removeList.forEach((el) => el.remove());'}
|
||||||
removeList = [];
|
removeList = [];
|
||||||
|
|
|
@ -41,4 +41,7 @@
|
||||||
|
|
||||||
[url=https://www.pornhub.com/view_video.php?viewkey=ph5b2c03dc1e23b]Test[/url]
|
[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
|
* Display match score in search results
|
||||||
* Current search filters are listed in the search dialog
|
* Current search filters are listed in the search dialog
|
||||||
* Search filters can be reset
|
* Search filters can be reset
|
||||||
|
* Last 15 searches are stored and can be accessed from the 'Search' dialog
|
||||||
* Character status
|
* Character status
|
||||||
* Last 10 status messages are stored and can be accessed from the 'Set status' dialog
|
* Last 10 status messages are stored and can be accessed from the 'Set status' dialog
|
||||||
* General
|
* General
|
||||||
|
|
Loading…
Reference in New Issue