Character preview cleanup

This commit is contained in:
Mr. Stallion 2020-10-30 17:47:33 -05:00
parent 5aa59e1dba
commit 1a12a3e57d
10 changed files with 158 additions and 54 deletions

View File

@ -11,7 +11,6 @@
@mouseover.prevent="show()"
@mouseenter.prevent="show()"
@mouseleave.prevent="dismiss()"
@mouseout.prevent="dismiss()"
@click.middle.prevent="toggleStickyness()"
>{{text}}</a>
<span

View File

@ -1,7 +1,7 @@
<template>
<modal :action="l('characterSearch.action')" @submit.prevent="submit()" dialogClass="w-100"
:buttonText="results ? l('characterSearch.again') : undefined" class="character-search">
<div v-if="options && !results">
:buttonText="state === 'results' ? l('characterSearch.again') : undefined" class="character-search">
<div v-if="options && state === 'search'">
<div v-show="error" class="alert alert-danger">{{error}}</div>
<filterable-select v-model="data.kinks" :multiple="true" :placeholder="l('filter')"
:title="l('characterSearch.kinks')" :filterFunc="filterKink" :options="options.kinks">
@ -27,19 +27,23 @@
<search-history ref="searchHistory" :callback="updateSearch" :curSearch="data"></search-history>
</div>
<div v-else-if="results" class="results">
<div v-else-if="state === 'results'" class="results">
<h4>
{{l('characterSearch.results')}}
<i class="fas fa-circle-notch fa-spin search-spinner" v-if="!resultsComplete"></i>
{{results.length}} {{l('characterSearch.results')}}
<span v-if="resultsPending > 0" class="pending">Scoring {{resultsPending}}... <i class="fas fa-circle-notch fa-spin search-spinner"></i></span>
</h4>
<div v-for="character in results" :key="character.name" class="search-result" :class="'status-' + character.status">
<template v-if="character.status === 'looking'" v-once>
<img :src="characterImage(character.name)" v-if="showAvatars"/>
<user :character="character" :showStatus="true" :match="shouldShowMatch"></user>
<bbcode :text="character.statusText"></bbcode>
<div v-for="record in results" :key="record.character.name" class="search-result" :class="'status-' + record.character.status">
<template v-if="record.character.status === 'looking'" v-once>
<img :src="characterImage(record.character.name)" v-if="showAvatars"/>
<user :character="record.character" :showStatus="true" :match="shouldShowMatch"></user>
<bbcode :text="record.character.statusText" class="status-text"></bbcode>
</template>
<template v-else v-once>
<user :character="record.character" :showStatus="true" :match="shouldShowMatch"></user>
<bbcode :text="record.character.statusText" v-if="!!record.character.statusText" class="status-text"></bbcode>
</template>
<user v-else :character="character" :showStatus="true" :match="shouldShowMatch" v-once></user>
</div>
</div>
</modal>
@ -62,6 +66,7 @@
import CharacterSearchHistory from './CharacterSearchHistory.vue';
import { Matcher } from '../learn/matcher';
import { nonAnthroSpecies, Species, speciesMapping, speciesNames } from '../learn/matcher-types';
import { CharacterCacheRecord } from '../learn/profile-cache';
type Options = {
kinks: SearchKink[],
@ -70,7 +75,16 @@
let options: Options | undefined;
function sort(x: Character, y: Character): number {
interface SearchResult {
character: Character;
profile: CharacterCacheRecord | null;
}
function sort(resultX: SearchResult, resultY: SearchResult): number {
const x = resultX.character;
const y = resultY.character;
if(x.status === 'looking' && y.status !== 'looking') return -1;
if(x.status !== 'looking' && y.status === 'looking') return 1;
@ -106,11 +120,12 @@
l = l;
kinksFilter = '';
error = '';
results: Character[] | undefined;
resultsComplete = false;
results: SearchResult[] = [];
resultsPending = 0;
characterImage = characterImage;
options!: ExtendedSearchData;
shouldShowMatch = true;
state = 'search';
data: ExtendedSearchData = {
kinks: [],
@ -150,6 +165,7 @@
});
}
@Hook('mounted')
mounted(): void {
core.connection.onMessage('ERR', (data) => {
@ -165,12 +181,12 @@
}
});
core.connection.onMessage('FKS', (data) => {
this.results = data.characters.map((x) => core.characters.get(x))
.filter((x) => core.state.hiddenUsers.indexOf(x.name) === -1 && !x.isIgnored)
this.results = data.characters.map((x) => ({ character: core.characters.get(x), profile: null }))
.filter((x) => core.state.hiddenUsers.indexOf(x.character.name) === -1 && !x.character.isIgnored)
.filter((x) => this.isSpeciesMatch(x))
.sort(sort);
this.resultsComplete = this.checkResultCompletion();
this.resultsPending = this.countPendingResults();
});
if (this.scoreWatcher) {
@ -182,18 +198,18 @@
// console.log('scoreWatcher', event);
if (
(this.results)
(this.state === 'results')
// tslint:disable-next-line no-unsafe-any no-any
&& (event.character)
// tslint:disable-next-line no-unsafe-any no-any
&& (_.find(this.results, (c: Character) => c.name === event.character.character.name))
&& (_.find(this.results, (s: SearchResult) => s.character.name === event.character.character.name))
) {
this.results = (_.filter(
this.results,
(x) => this.isSpeciesMatch(x)
) as Character[]).sort(sort);
) as SearchResult[]).sort(sort);
this.resultsComplete = this.checkResultCompletion();
this.resultsPending = this.countPendingResults();
}
};
@ -231,17 +247,20 @@
}
isSpeciesMatch(c: Character): boolean {
isSpeciesMatch(result: SearchResult): boolean {
if (this.data.species.length === 0) {
return true;
}
const knownCharacter = core.cache.profileCache.getSync(c.name);
const knownCharacter = core.cache.profileCache.getSync(result.character.name);
if (!knownCharacter) {
return true;
}
// optimization
result.profile = knownCharacter;
const isSearchingForAnthro = (!!_.find(this.data.species, (s) => s.id === Species.Anthro));
const isSearchingForHuman = (!!_.find(this.data.species, (s) => s.id === Species.Human));
@ -298,10 +317,18 @@
}
checkResultCompletion(): boolean {
return _.every(
countPendingResults(): number {
return _.reduce(
this.results,
(c: Character) => (!!core.cache.profileCache.getSync(c.name))
(accum: number, result: SearchResult) => {
if (!!result.profile) {
return accum;
}
result.profile = core.cache.profileCache.getSync(result.character.name);
return !!result.profile ? accum : accum + 1;
},
0
);
}
@ -351,13 +378,17 @@
submit(): void {
if(this.results !== undefined) {
this.results = undefined;
if(this.state === 'results') {
this.results = [];
this.state = 'search';
return;
}
this.shouldShowMatch = core.state.settings.risingComparisonInSearch;
this.results = [];
this.state = 'results';
this.error = '';
const data: Connection.ClientCommands['FKS'] & {[key: string]: (string | number)[]} = {kinks: []};
for(const key in this.data) {
@ -407,7 +438,7 @@
.results {
.user-view {
display: block;
// display: block;
}
& > .search-result {
clear: both;
@ -415,12 +446,38 @@
& > .status-looking {
margin-bottom: 5px;
min-height: 50px;
.status-text {
display: block;
}
}
& > .status-offline,
& > .status-online,
& > .status-away,
& > .status-idle,
& > .status-busy,
& > .status-dnd,
& > .status-crown {
overflow: hidden;
width: 100%;
height: 23px;
.status-text {
opacity: 0.75;
padding-left: 4px;
}
}
img {
float: left;
margin-right: 5px;
width: 50px;
}
.search-result:nth-child(2n) {
background-color: rgba(0,0,0, 0.15);
}
}
.search-string {
@ -434,8 +491,14 @@
font-weight: bold;
}
.search-spinner {
.pending {
float: right;
color: var(--gray);
font-size: 80%;
}
.search-spinner {
// float: right;
}
}
</style>

View File

@ -1,5 +1,5 @@
<!-- Linebreaks inside this template will break BBCode views -->
<template><span :class="userClass" v-bind:bbcodeTag.prop="'user'" v-bind:character.prop="character" v-bind:channel.prop="channel" @mouseover.prevent="show()" @mouseenter.prevent="show()" @mouseleave.prevent="dismiss()" @mouseout.prevent="dismiss()" @click.middle.prevent="toggleStickyness()" @click.right.passive="dismiss(true)" @click.left.passive="dismiss(true)"><span v-if="!!statusClass" :class="statusClass"></span><span v-if="!!rankIcon" :class="rankIcon"></span>{{character.name}}<span v-if="!!matchClass" :class="matchClass">{{getMatchScoreTitle(matchScore)}}</span></span></template>
<template><span :class="userClass" v-bind:bbcodeTag.prop="'user'" v-bind:character.prop="character" v-bind:channel.prop="channel" @mouseover.prevent="show()" @mouseenter.prevent="show()" @mouseleave.prevent="dismiss()" @click.middle.prevent="toggleStickyness()" @click.right.passive="dismiss(true)" @click.left.passive="dismiss(true)"><span v-if="!!statusClass" :class="statusClass"></span><span v-if="!!rankIcon" :class="rankIcon"></span>{{character.name}}<span v-if="!!matchClass" :class="matchClass">{{getMatchScoreTitle(matchScore)}}</span></span></template>
<script lang="ts">

View File

@ -5,7 +5,7 @@
<img :src="avatarUrl(character.character.name)" class="character-avatar">
</div>
<div class="col-8">
<div class="col-10">
<h1><span class="character-name" :class="(statusClasses || {}).userClass">{{ character.character.name }}</span></h1>
<h3>{{ getOnlineStatus() }}</h3>
@ -24,11 +24,11 @@
<match-tags v-if="match" :match="match"></match-tags>
<div class="status-message" v-if="statusMessage">
<h4>Status</h4>
<h4>Status <span v-if="latestAd && (statusMessage === latestAd.message)">&amp; Latest Ad</span></h4>
<bbcode :text="statusMessage"></bbcode>
</div>
<div v-if="latestAd">
<div class="latest-ad-message" v-if="latestAd && (latestAd.message !== statusMessage)">
<h4>Latest Ad <span class="message-time">{{formatTime(latestAd.datePosted)}}</span></h4>
<bbcode :text="latestAd.message"></bbcode>
</div>
@ -166,11 +166,15 @@ export default class CharacterPreview extends Vue {
const rawSpecies = Matcher.getTagValue(TagId.Species, c);
const rawAge = Matcher.getTagValue(TagId.Age, c);
if ((a.species) && (!Species[a.species])) {
console.log('SPECIES', a.species, rawSpecies);
// if ((a.species) && (!Species[a.species])) {
// console.log('SPECIES', a.species, rawSpecies);
// }
if ((a.orientation) && (!Orientation[a.orientation])) {
console.error('Missing Orientation', a.orientation, c.name);
}
this.age = a.age ? this.readable(`${a.age}`) : (rawAge && rawAge.string) || undefined;
this.age = a.age ? this.readable(`${a.age}`) : (rawAge && /[0-9]/.test(rawAge.string || '') && rawAge.string) || undefined;
this.species = a.species ? this.readable(Species[a.species]) : (rawSpecies && rawSpecies.string) || undefined;
this.gender = a.gender ? this.readable(Gender[a.gender]) : undefined;
this.furryPref = a.furryPreference ? this.readable(furryPreferenceMapping[a.furryPreference]) : undefined;
@ -228,7 +232,13 @@ export default class CharacterPreview extends Vue {
<style lang="scss">
.character-preview {
padding: 10px;
padding-right: 15px;
background-color: var(--input-bg);
max-height: 100%;
overflow: hidden;
opacity: 0.95;
border-radius: 0 5px 5px 5px;
border: 1px solid var(--secondary);
.summary {
font-size: 125%;
@ -279,7 +289,6 @@ export default class CharacterPreview extends Vue {
h4 {
font-size: 1.25rem;
margin-top: 1.3rem;
margin-bottom: 0;
.message-time {
@ -290,6 +299,15 @@ export default class CharacterPreview extends Vue {
}
}
.status-message,
.latest-ad-message {
display: block;
background-color: rgba(0,0,0,0.2);
padding: 10px;
border-radius: 5px;
margin-top: 1.3rem;
}
.character-avatar {
width: 100%;
height: auto;

View File

@ -63,6 +63,8 @@
const screen = remote.screen;
const FLIST_PROFILE_MATCH = _.cloneDeep(/https?:\/\/(www.)?f-list.net\/c\/([a-zA-Z0-9+%_.!~*'()]+)\/?/);
interface DidFailLoadEvent extends Event {
errorCode: number;
errorDescription: string;
@ -135,7 +137,7 @@
'imagepreview-dismiss',
(eventData: EventBusEvent) => {
// console.log('Event dismiss', eventData.url);
this.dismiss(eventData.url as string, eventData.force as boolean);
this.dismiss(this.negotiateUrl(eventData.url as string || ''), eventData.force as boolean);
}
);
@ -144,11 +146,17 @@
(eventData: EventBusEvent) => {
// console.log('Event show', eventData.url);
if (!core.state.settings.risingLinkPreview) {
return;
const url = this.negotiateUrl(eventData.url as string || '');
const isInternalPreview = CharacterPreviewHelper.FLIST_CHARACTER_PROTOCOL_TESTER.test(url);
if (
((!core.state.settings.risingCharacterPreview) && (isInternalPreview))
|| ((!core.state.settings.risingLinkPreview) && (!isInternalPreview))
) {
return;
}
this.show(eventData.url as string);
this.show(url);
}
);
@ -159,7 +167,7 @@
return;
}
const eventUrl = this.jsMutator.mutateUrl(eventData.url as string);
const eventUrl = this.jsMutator.mutateUrl(this.negotiateUrl(eventData.url as string || ''));
if ((this.url === eventUrl) && (this.visible))
this.sticky = !this.sticky;
@ -327,6 +335,16 @@
}
negotiateUrl(url: string): string {
const match = url.match(FLIST_PROFILE_MATCH);
if (!match) {
return url;
}
return `flist-character://${decodeURI(match[2])}`;
}
updatePreviewSize(width: number, height: number): void {
const helper = this.previewManager.getVisiblePreview();
@ -392,7 +410,7 @@
if ((!this.hasMouseMovedSince()) && (!force))
return;
this.debugLog('ImagePreview: dismiss.exec', this.previewManager.getVisibilityStatus(), url);
this.debugLog('ImagePreview: dismiss.exec', due, this.previewManager.getVisibilityStatus(), url);
// This timeout makes the preview window disappear with a slight delay, which helps UX
// when dealing with situations such as quickly scrolling text that moves the cursor away

View File

@ -137,6 +137,7 @@ export class ImageDomMutator {
async init(): Promise<void> {
await this.loadScripts();
/* tslint:disable max-line-length */
this.add('default', this.getBaseJsMutatorScript(['.content video', '.content img', '#video, video', '#image, img']));
this.add('about:blank', '');
this.add('e621.net', this.getBaseJsMutatorScript(['video', '#image']));
@ -170,6 +171,7 @@ export class ImageDomMutator {
this.add('redgifs.com', this.getBaseJsMutatorScript(['video']));
this.add('furaffinity.net', this.getBaseJsMutatorScript(['#submissionImg', 'video', 'img']));
this.add('rule34.paheal.net', this.getBaseJsMutatorScript(['#main_image', 'video', 'img']));
this.add('xhamster.com', this.getBaseJsMutatorScript(['#photo_slider video', '#photo_slider img', 'video', 'img']));
this.add(
'pornhub.com',

View File

@ -93,6 +93,9 @@
[url=https://www.furaffinity.net/view/38851727/]Furaffinity[/url]
[url=https://xhamster.com/photos/gallery/15416548/465700664]xHamster Gallery[/url]
[url=https://xhamster.com/videos/letsdoeit-check-out-the-sexiest-massage-sex-compilation-now-xh7hyIG]xHamster Video[/url]
Broken
https://vimeo.com/265884960

View File

@ -38,6 +38,7 @@ export enum Orientation {
Bisexual = 6,
Asexual = 7,
Unsure = 8,
Cuntboy = 59,
BiMalePreference = 89,
BiFemalePreference = 90,
Pansexual = 127,
@ -241,7 +242,7 @@ export const speciesMapping: SpeciesMap = {
gen('(beast|anthro|furry)')
],
[Species.Human]: ['human', 'homo sapiens', 'human.*', 'homo[ -]?sapi[ea]ns?', 'woman', 'h[o]+m[ai]n', 'humaine?',
[Species.Human]: ['human', 'homo sapiens', 'human.*', 'homo[ -]?sapi[ea]ns?', 'woman', 'h[uo]+m[aie]n', 'humaine?',
'meat[ -]?popsicle',
// where should these go?
'angel', 'neph[ai]l[ei]m', 'arch[ -]?angel'
@ -255,8 +256,8 @@ export const speciesMapping: SpeciesMap = {
[Species.Canine]: ['dog', 'dingo', 'coyote', 'jackal', 'husky', 'canine', 'wolf.*', 'doberman[n]?', 'hound', 'akita', 'pit ?bull',
'terrier', 'bull[ -]?terrier', 'australian[ -]?shepherd', 'australian[ -]?shep[h]?ard', 'german[ -]?shep[h]?([ea]rd)?',
'malinois', 'woof', 'labrador', 'collie', 'canis', 'lupus', 'canid', 'chihuahua', 'poodle', 'chinchilla',
'chow[ -]?chow', 'corgi', 'anubis', 'beagle', '.*wolf', 'direwolf', 'pointer', 'dhole',
'anubian', 'dalmatian', 'dalmation', 'inumimi', 'lupine', 'malamute', 'mastiff', 'mutt', 'rottweill?er', 'shih[ -]?tzu', 'worgen',
'chow[ -]?chow', 'corgi', 'anubis', 'beagle', '.*wolf', 'direwolf', 'pointer', 'dhole', 'worg(en)?',
'anubian', 'dalmatian', 'dalmation', 'inumimi', 'lupine', 'malamute', 'mastiff', 'mutt', 'rottweill?er', 'shih[ -]?tzu',
'vallhund', 'puppy', 'oo?kami', 'great[ -]?dane', 'golden[ -]?(retriever|lab|labrador)', 'cocker[ -]?spaniel', 'samoyed', 'awoo',
'borzoi', 'spaniel', 'ookamimimi', 'jakkarumimi', 'chinchiramimi', 'woffo', 'wuff', 'wolfdog', 'setter', 'papillon',
'🐶', '🐺', '🐕', '🐩', 'aussie[ -]?doodle', 'shiba', 'inu', 'veil[ -]?hound', 'timber[ -]?wolf', 'hell[ -]?hound', 'hound',
@ -296,10 +297,10 @@ export const speciesMapping: SpeciesMap = {
gen('dragon')
],
[Species.Reptile]: ['lizard', 'snake', 'crocodile', 'alligator', 'chameleon', 'anole', 'aligator', 'snake.*', 'gator',
[Species.Reptile]: ['lizard', 'snake', 'crocodile', 'alligator', 'chameleon', 'anole', '(all?i)?gator', 'snake.*', 'gator',
'gecko', 'reptile', 'reptilian', 'scaly', 'scale[ -]?born', 'argonian', 'saxhleel', 'skink', 'cobra', 'turtle', 'tortoise',
'nope[ -]rope', 'anaconda', 'python', gen('(lizard|snake)'), 'yuan[ -]?ti', 'crocodilian',
'serp[ea]a?nt(ine)?', 'viper', 'titanoboa', 'boa', 'taipan',
'nope[ -]rope', 'anaconda', 'python', gen('(lizard|snake|croc)'), 'yuan[ -]?ti', 'crocodilian',
'serp[ea]a?nt(ine)?', 'viper', 'titanoboa', 'boa', 'taipan', 'croc',
'ludroth', 'zvarr', '🐍'],
[Species.Pokemon]: ['charizard', 'charmander', 'pikachu', 'digimon', 'renamon', 'eevee', 'gardev(oi|io)r', 'absol', 'aggron',
@ -332,7 +333,7 @@ export const speciesMapping: SpeciesMap = {
[Species.Bovine]: ['cow', 'bison', 'bovine', 'antelope', 'gazelle', 'oryx', 'buffalo', 'bison', 'black[ -]?angus', 'bull', 'ox',
'ushimimi', 'holstaur', 'moo', 'cattle', 'hucow', 'caprin[a]?e', 'goat[ -]?antelope', 'muskox', 'urial', 'mouflon',
'taurine', 'aurochs', 'bos', 'bos taurus', 'taurus',
'taurine', 'aurochs', 'bos', 'bos taurus', 'taur[u|o]s',
'🐃', '🐂', '🐄', '🐐'],
[Species.Caprinae]: ['sheep', 'goat', 'ibex', 'takin', 'bharal', 'goral', 'serow', 'lamb', 'faun', 'ram', 'faunus', 'goat.*',

View File

@ -806,7 +806,7 @@ export class Matcher {
const s = Matcher.getMappedSpecies(mySpecies.string);
if (!s) {
log.silly('matcher.species.unknown', { character: c.name, species: mySpecies.string });
log.debug('matcher.species.unknown', { character: c.name, species: mySpecies.string });
}
return s;

View File

@ -1,7 +1,7 @@
<template>
<div id="character-page-sidebar" class="card bg-light">
<div class="card-body">
<img :src="avatarUrl(character.character.name)" class="character-avatar" style="margin-right:10px">
<img :src="avatarUrl(character.character.name)" class="character-avatar" style="width: 100%; height: auto;">
<div v-if="character.character.title" class="character-title">{{ character.character.title }}</div>
<character-action-menu :character="character" @rename="showRename()" @delete="showDelete()"