Character preview cleanup
This commit is contained in:
		
							parent
							
								
									5aa59e1dba
								
							
						
					
					
						commit
						1a12a3e57d
					
				@ -11,7 +11,6 @@
 | 
				
			|||||||
            @mouseover.prevent="show()"
 | 
					            @mouseover.prevent="show()"
 | 
				
			||||||
            @mouseenter.prevent="show()"
 | 
					            @mouseenter.prevent="show()"
 | 
				
			||||||
            @mouseleave.prevent="dismiss()"
 | 
					            @mouseleave.prevent="dismiss()"
 | 
				
			||||||
            @mouseout.prevent="dismiss()"
 | 
					 | 
				
			||||||
            @click.middle.prevent="toggleStickyness()"
 | 
					            @click.middle.prevent="toggleStickyness()"
 | 
				
			||||||
        >{{text}}</a>
 | 
					        >{{text}}</a>
 | 
				
			||||||
        <span
 | 
					        <span
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,7 @@
 | 
				
			|||||||
<template>
 | 
					<template>
 | 
				
			||||||
    <modal :action="l('characterSearch.action')" @submit.prevent="submit()" dialogClass="w-100"
 | 
					    <modal :action="l('characterSearch.action')" @submit.prevent="submit()" dialogClass="w-100"
 | 
				
			||||||
        :buttonText="results ? l('characterSearch.again') : undefined" class="character-search">
 | 
					        :buttonText="state === 'results' ? l('characterSearch.again') : undefined" class="character-search">
 | 
				
			||||||
        <div v-if="options && !results">
 | 
					        <div v-if="options && state === 'search'">
 | 
				
			||||||
            <div v-show="error" class="alert alert-danger">{{error}}</div>
 | 
					            <div v-show="error" class="alert alert-danger">{{error}}</div>
 | 
				
			||||||
            <filterable-select v-model="data.kinks" :multiple="true" :placeholder="l('filter')"
 | 
					            <filterable-select v-model="data.kinks" :multiple="true" :placeholder="l('filter')"
 | 
				
			||||||
                :title="l('characterSearch.kinks')" :filterFunc="filterKink" :options="options.kinks">
 | 
					                :title="l('characterSearch.kinks')" :filterFunc="filterKink" :options="options.kinks">
 | 
				
			||||||
@ -27,19 +27,23 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            <search-history ref="searchHistory" :callback="updateSearch" :curSearch="data"></search-history>
 | 
					            <search-history ref="searchHistory" :callback="updateSearch" :curSearch="data"></search-history>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
        <div v-else-if="results" class="results">
 | 
					        <div v-else-if="state === 'results'" class="results">
 | 
				
			||||||
            <h4>
 | 
					            <h4>
 | 
				
			||||||
                {{l('characterSearch.results')}}
 | 
					                {{results.length}} {{l('characterSearch.results')}}
 | 
				
			||||||
                <i class="fas fa-circle-notch fa-spin search-spinner" v-if="!resultsComplete"></i>
 | 
					
 | 
				
			||||||
 | 
					                <span v-if="resultsPending > 0" class="pending">Scoring {{resultsPending}}... <i class="fas fa-circle-notch fa-spin search-spinner"></i></span>
 | 
				
			||||||
            </h4>
 | 
					            </h4>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <div v-for="character in results" :key="character.name" class="search-result" :class="'status-' + character.status">
 | 
					            <div v-for="record in results" :key="record.character.name" class="search-result" :class="'status-' + record.character.status">
 | 
				
			||||||
                <template v-if="character.status === 'looking'" v-once>
 | 
					                <template v-if="record.character.status === 'looking'" v-once>
 | 
				
			||||||
                    <img :src="characterImage(character.name)" v-if="showAvatars"/>
 | 
					                    <img :src="characterImage(record.character.name)" v-if="showAvatars"/>
 | 
				
			||||||
                    <user :character="character" :showStatus="true" :match="shouldShowMatch"></user>
 | 
					                    <user :character="record.character" :showStatus="true" :match="shouldShowMatch"></user>
 | 
				
			||||||
                    <bbcode :text="character.statusText"></bbcode>
 | 
					                    <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>
 | 
					                </template>
 | 
				
			||||||
                <user v-else :character="character" :showStatus="true" :match="shouldShowMatch" v-once></user>
 | 
					 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
    </modal>
 | 
					    </modal>
 | 
				
			||||||
@ -62,6 +66,7 @@
 | 
				
			|||||||
    import CharacterSearchHistory from './CharacterSearchHistory.vue';
 | 
					    import CharacterSearchHistory from './CharacterSearchHistory.vue';
 | 
				
			||||||
    import { Matcher } from '../learn/matcher';
 | 
					    import { Matcher } from '../learn/matcher';
 | 
				
			||||||
    import { nonAnthroSpecies, Species, speciesMapping, speciesNames } from '../learn/matcher-types';
 | 
					    import { nonAnthroSpecies, Species, speciesMapping, speciesNames } from '../learn/matcher-types';
 | 
				
			||||||
 | 
					    import { CharacterCacheRecord } from '../learn/profile-cache';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    type Options = {
 | 
					    type Options = {
 | 
				
			||||||
        kinks: SearchKink[],
 | 
					        kinks: SearchKink[],
 | 
				
			||||||
@ -70,7 +75,16 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    let options: Options | undefined;
 | 
					    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;
 | 
				
			||||||
        if(x.status !== 'looking' && y.status === 'looking') return 1;
 | 
					        if(x.status !== 'looking' && y.status === 'looking') return 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -106,11 +120,12 @@
 | 
				
			|||||||
        l = l;
 | 
					        l = l;
 | 
				
			||||||
        kinksFilter = '';
 | 
					        kinksFilter = '';
 | 
				
			||||||
        error = '';
 | 
					        error = '';
 | 
				
			||||||
        results: Character[] | undefined;
 | 
					        results: SearchResult[] = [];
 | 
				
			||||||
        resultsComplete = false;
 | 
					        resultsPending = 0;
 | 
				
			||||||
        characterImage = characterImage;
 | 
					        characterImage = characterImage;
 | 
				
			||||||
        options!: ExtendedSearchData;
 | 
					        options!: ExtendedSearchData;
 | 
				
			||||||
        shouldShowMatch = true;
 | 
					        shouldShowMatch = true;
 | 
				
			||||||
 | 
					        state = 'search';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        data: ExtendedSearchData = {
 | 
					        data: ExtendedSearchData = {
 | 
				
			||||||
            kinks: [],
 | 
					            kinks: [],
 | 
				
			||||||
@ -150,6 +165,7 @@
 | 
				
			|||||||
            });
 | 
					            });
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        @Hook('mounted')
 | 
					        @Hook('mounted')
 | 
				
			||||||
        mounted(): void {
 | 
					        mounted(): void {
 | 
				
			||||||
            core.connection.onMessage('ERR', (data) => {
 | 
					            core.connection.onMessage('ERR', (data) => {
 | 
				
			||||||
@ -165,12 +181,12 @@
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
            core.connection.onMessage('FKS', (data) => {
 | 
					            core.connection.onMessage('FKS', (data) => {
 | 
				
			||||||
                this.results = data.characters.map((x) => core.characters.get(x))
 | 
					                this.results = data.characters.map((x) => ({ character: core.characters.get(x), profile: null }))
 | 
				
			||||||
                    .filter((x) => core.state.hiddenUsers.indexOf(x.name) === -1 && !x.isIgnored)
 | 
					                    .filter((x) => core.state.hiddenUsers.indexOf(x.character.name) === -1 && !x.character.isIgnored)
 | 
				
			||||||
                    .filter((x) => this.isSpeciesMatch(x))
 | 
					                    .filter((x) => this.isSpeciesMatch(x))
 | 
				
			||||||
                    .sort(sort);
 | 
					                    .sort(sort);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                this.resultsComplete = this.checkResultCompletion();
 | 
					                this.resultsPending = this.countPendingResults();
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (this.scoreWatcher) {
 | 
					            if (this.scoreWatcher) {
 | 
				
			||||||
@ -182,18 +198,18 @@
 | 
				
			|||||||
                // console.log('scoreWatcher', event);
 | 
					                // console.log('scoreWatcher', event);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if (
 | 
					                if (
 | 
				
			||||||
                    (this.results)
 | 
					                    (this.state === 'results')
 | 
				
			||||||
                    // tslint:disable-next-line no-unsafe-any no-any
 | 
					                    // tslint:disable-next-line no-unsafe-any no-any
 | 
				
			||||||
                    && (event.character)
 | 
					                    && (event.character)
 | 
				
			||||||
                    // tslint:disable-next-line no-unsafe-any no-any
 | 
					                    // 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 = (_.filter(
 | 
				
			||||||
                        this.results,
 | 
					                        this.results,
 | 
				
			||||||
                        (x) => this.isSpeciesMatch(x)
 | 
					                        (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) {
 | 
					          if (this.data.species.length === 0) {
 | 
				
			||||||
            return true;
 | 
					            return true;
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          const knownCharacter = core.cache.profileCache.getSync(c.name);
 | 
					          const knownCharacter = core.cache.profileCache.getSync(result.character.name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          if (!knownCharacter) {
 | 
					          if (!knownCharacter) {
 | 
				
			||||||
            return true;
 | 
					            return true;
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          // optimization
 | 
				
			||||||
 | 
					          result.profile = knownCharacter;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          const isSearchingForAnthro = (!!_.find(this.data.species, (s) => s.id === Species.Anthro));
 | 
					          const isSearchingForAnthro = (!!_.find(this.data.species, (s) => s.id === Species.Anthro));
 | 
				
			||||||
          const isSearchingForHuman = (!!_.find(this.data.species, (s) => s.id === Species.Human));
 | 
					          const isSearchingForHuman = (!!_.find(this.data.species, (s) => s.id === Species.Human));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -298,10 +317,18 @@
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        checkResultCompletion(): boolean {
 | 
					        countPendingResults(): number {
 | 
				
			||||||
            return _.every(
 | 
					            return _.reduce(
 | 
				
			||||||
                this.results,
 | 
					                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 {
 | 
					        submit(): void {
 | 
				
			||||||
            if(this.results !== undefined) {
 | 
					            if(this.state === 'results') {
 | 
				
			||||||
                this.results = undefined;
 | 
					                this.results = [];
 | 
				
			||||||
 | 
					                this.state = 'search';
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            this.shouldShowMatch = core.state.settings.risingComparisonInSearch;
 | 
					            this.shouldShowMatch = core.state.settings.risingComparisonInSearch;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            this.results = [];
 | 
				
			||||||
 | 
					            this.state = 'results';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            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) {
 | 
				
			||||||
@ -407,7 +438,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        .results {
 | 
					        .results {
 | 
				
			||||||
            .user-view {
 | 
					            .user-view {
 | 
				
			||||||
                display: block;
 | 
					                // display: block;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            & > .search-result {
 | 
					            & > .search-result {
 | 
				
			||||||
                clear: both;
 | 
					                clear: both;
 | 
				
			||||||
@ -415,12 +446,38 @@
 | 
				
			|||||||
            & > .status-looking {
 | 
					            & > .status-looking {
 | 
				
			||||||
                margin-bottom: 5px;
 | 
					                margin-bottom: 5px;
 | 
				
			||||||
                min-height: 50px;
 | 
					                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 {
 | 
					            img {
 | 
				
			||||||
                float: left;
 | 
					                float: left;
 | 
				
			||||||
                margin-right: 5px;
 | 
					                margin-right: 5px;
 | 
				
			||||||
                width: 50px;
 | 
					                width: 50px;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            .search-result:nth-child(2n) {
 | 
				
			||||||
 | 
					                background-color: rgba(0,0,0, 0.15);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        .search-string {
 | 
					        .search-string {
 | 
				
			||||||
@ -434,8 +491,14 @@
 | 
				
			|||||||
            font-weight: bold;
 | 
					            font-weight: bold;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        .search-spinner {
 | 
					        .pending {
 | 
				
			||||||
            float: right;
 | 
					            float: right;
 | 
				
			||||||
 | 
					            color: var(--gray);
 | 
				
			||||||
 | 
					            font-size: 80%;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .search-spinner {
 | 
				
			||||||
 | 
					            // float: right;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
</style>
 | 
					</style>
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,5 @@
 | 
				
			|||||||
<!-- Linebreaks inside this template will break BBCode views -->
 | 
					<!-- 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">
 | 
					<script lang="ts">
 | 
				
			||||||
 | 
				
			|||||||
@ -5,7 +5,7 @@
 | 
				
			|||||||
        <img :src="avatarUrl(character.character.name)" class="character-avatar">
 | 
					        <img :src="avatarUrl(character.character.name)" class="character-avatar">
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      <div class="col-8">
 | 
					      <div class="col-10">
 | 
				
			||||||
        <h1><span class="character-name" :class="(statusClasses || {}).userClass">{{ character.character.name }}</span></h1>
 | 
					        <h1><span class="character-name" :class="(statusClasses || {}).userClass">{{ character.character.name }}</span></h1>
 | 
				
			||||||
        <h3>{{ getOnlineStatus() }}</h3>
 | 
					        <h3>{{ getOnlineStatus() }}</h3>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -24,11 +24,11 @@
 | 
				
			|||||||
        <match-tags v-if="match" :match="match"></match-tags>
 | 
					        <match-tags v-if="match" :match="match"></match-tags>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <div class="status-message" v-if="statusMessage">
 | 
					        <div class="status-message" v-if="statusMessage">
 | 
				
			||||||
          <h4>Status</h4>
 | 
					          <h4>Status <span v-if="latestAd && (statusMessage === latestAd.message)">& Latest Ad</span></h4>
 | 
				
			||||||
          <bbcode :text="statusMessage"></bbcode>
 | 
					          <bbcode :text="statusMessage"></bbcode>
 | 
				
			||||||
        </div>
 | 
					        </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>
 | 
					          <h4>Latest Ad <span class="message-time">{{formatTime(latestAd.datePosted)}}</span></h4>
 | 
				
			||||||
          <bbcode :text="latestAd.message"></bbcode>
 | 
					          <bbcode :text="latestAd.message"></bbcode>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
@ -166,11 +166,15 @@ export default class CharacterPreview extends Vue {
 | 
				
			|||||||
    const rawSpecies = Matcher.getTagValue(TagId.Species, c);
 | 
					    const rawSpecies = Matcher.getTagValue(TagId.Species, c);
 | 
				
			||||||
    const rawAge = Matcher.getTagValue(TagId.Age, c);
 | 
					    const rawAge = Matcher.getTagValue(TagId.Age, c);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if ((a.species) && (!Species[a.species])) {
 | 
					    // if ((a.species) && (!Species[a.species])) {
 | 
				
			||||||
      console.log('SPECIES', a.species, rawSpecies);
 | 
					      // 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.species = a.species ? this.readable(Species[a.species]) : (rawSpecies && rawSpecies.string) || undefined;
 | 
				
			||||||
    this.gender = a.gender ? this.readable(Gender[a.gender]) : undefined;
 | 
					    this.gender = a.gender ? this.readable(Gender[a.gender]) : undefined;
 | 
				
			||||||
    this.furryPref = a.furryPreference ? this.readable(furryPreferenceMapping[a.furryPreference]) : undefined;
 | 
					    this.furryPref = a.furryPreference ? this.readable(furryPreferenceMapping[a.furryPreference]) : undefined;
 | 
				
			||||||
@ -228,7 +232,13 @@ export default class CharacterPreview extends Vue {
 | 
				
			|||||||
<style lang="scss">
 | 
					<style lang="scss">
 | 
				
			||||||
  .character-preview {
 | 
					  .character-preview {
 | 
				
			||||||
    padding: 10px;
 | 
					    padding: 10px;
 | 
				
			||||||
 | 
					    padding-right: 15px;
 | 
				
			||||||
    background-color: var(--input-bg);
 | 
					    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 {
 | 
					    .summary {
 | 
				
			||||||
      font-size: 125%;
 | 
					      font-size: 125%;
 | 
				
			||||||
@ -279,7 +289,6 @@ export default class CharacterPreview extends Vue {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    h4 {
 | 
					    h4 {
 | 
				
			||||||
      font-size: 1.25rem;
 | 
					      font-size: 1.25rem;
 | 
				
			||||||
      margin-top: 1.3rem;
 | 
					 | 
				
			||||||
      margin-bottom: 0;
 | 
					      margin-bottom: 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      .message-time {
 | 
					      .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 {
 | 
					    .character-avatar {
 | 
				
			||||||
      width: 100%;
 | 
					      width: 100%;
 | 
				
			||||||
      height: auto;
 | 
					      height: auto;
 | 
				
			||||||
 | 
				
			|||||||
@ -63,6 +63,8 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    const screen = remote.screen;
 | 
					    const screen = remote.screen;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const FLIST_PROFILE_MATCH = _.cloneDeep(/https?:\/\/(www.)?f-list.net\/c\/([a-zA-Z0-9+%_.!~*'()]+)\/?/);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    interface DidFailLoadEvent extends Event {
 | 
					    interface DidFailLoadEvent extends Event {
 | 
				
			||||||
        errorCode: number;
 | 
					        errorCode: number;
 | 
				
			||||||
        errorDescription: string;
 | 
					        errorDescription: string;
 | 
				
			||||||
@ -135,7 +137,7 @@
 | 
				
			|||||||
                'imagepreview-dismiss',
 | 
					                'imagepreview-dismiss',
 | 
				
			||||||
                (eventData: EventBusEvent) => {
 | 
					                (eventData: EventBusEvent) => {
 | 
				
			||||||
                    // console.log('Event dismiss', eventData.url);
 | 
					                    // 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) => {
 | 
					                (eventData: EventBusEvent) => {
 | 
				
			||||||
                    // console.log('Event show', eventData.url);
 | 
					                    // console.log('Event show', eventData.url);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    if (!core.state.settings.risingLinkPreview) {
 | 
					                    const url = this.negotiateUrl(eventData.url as string || '');
 | 
				
			||||||
                        return;
 | 
					                    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;
 | 
					                        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))
 | 
					                    if ((this.url === eventUrl) && (this.visible))
 | 
				
			||||||
                        this.sticky = !this.sticky;
 | 
					                        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 {
 | 
					        updatePreviewSize(width: number, height: number): void {
 | 
				
			||||||
            const helper = this.previewManager.getVisiblePreview();
 | 
					            const helper = this.previewManager.getVisiblePreview();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -392,7 +410,7 @@
 | 
				
			|||||||
            if ((!this.hasMouseMovedSince()) && (!force))
 | 
					            if ((!this.hasMouseMovedSince()) && (!force))
 | 
				
			||||||
                return;
 | 
					                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
 | 
					            // 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
 | 
					            // when dealing with situations such as quickly scrolling text that moves the cursor away
 | 
				
			||||||
 | 
				
			|||||||
@ -137,6 +137,7 @@ export class ImageDomMutator {
 | 
				
			|||||||
    async init(): Promise<void> {
 | 
					    async init(): Promise<void> {
 | 
				
			||||||
        await this.loadScripts();
 | 
					        await this.loadScripts();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /* tslint:disable max-line-length */
 | 
				
			||||||
        this.add('default', this.getBaseJsMutatorScript(['.content video', '.content img', '#video, video', '#image, img']));
 | 
					        this.add('default', this.getBaseJsMutatorScript(['.content video', '.content img', '#video, video', '#image, img']));
 | 
				
			||||||
        this.add('about:blank', '');
 | 
					        this.add('about:blank', '');
 | 
				
			||||||
        this.add('e621.net', this.getBaseJsMutatorScript(['video', '#image']));
 | 
					        this.add('e621.net', this.getBaseJsMutatorScript(['video', '#image']));
 | 
				
			||||||
@ -170,6 +171,7 @@ export class ImageDomMutator {
 | 
				
			|||||||
        this.add('redgifs.com', this.getBaseJsMutatorScript(['video']));
 | 
					        this.add('redgifs.com', this.getBaseJsMutatorScript(['video']));
 | 
				
			||||||
        this.add('furaffinity.net', this.getBaseJsMutatorScript(['#submissionImg', 'video', 'img']));
 | 
					        this.add('furaffinity.net', this.getBaseJsMutatorScript(['#submissionImg', 'video', 'img']));
 | 
				
			||||||
        this.add('rule34.paheal.net', this.getBaseJsMutatorScript(['#main_image', '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(
 | 
					        this.add(
 | 
				
			||||||
            'pornhub.com',
 | 
					            'pornhub.com',
 | 
				
			||||||
 | 
				
			|||||||
@ -93,6 +93,9 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    [url=https://www.furaffinity.net/view/38851727/]Furaffinity[/url]
 | 
					    [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
 | 
					Broken
 | 
				
			||||||
https://vimeo.com/265884960
 | 
					https://vimeo.com/265884960
 | 
				
			||||||
 | 
				
			|||||||
@ -38,6 +38,7 @@ export enum Orientation {
 | 
				
			|||||||
    Bisexual = 6,
 | 
					    Bisexual = 6,
 | 
				
			||||||
    Asexual = 7,
 | 
					    Asexual = 7,
 | 
				
			||||||
    Unsure = 8,
 | 
					    Unsure = 8,
 | 
				
			||||||
 | 
					    Cuntboy = 59,
 | 
				
			||||||
    BiMalePreference = 89,
 | 
					    BiMalePreference = 89,
 | 
				
			||||||
    BiFemalePreference = 90,
 | 
					    BiFemalePreference = 90,
 | 
				
			||||||
    Pansexual = 127,
 | 
					    Pansexual = 127,
 | 
				
			||||||
@ -241,7 +242,7 @@ export const speciesMapping: SpeciesMap = {
 | 
				
			|||||||
        gen('(beast|anthro|furry)')
 | 
					        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',
 | 
					        'meat[ -]?popsicle',
 | 
				
			||||||
        // where should these go?
 | 
					        // where should these go?
 | 
				
			||||||
        'angel', 'neph[ai]l[ei]m', 'arch[ -]?angel'
 | 
					        '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',
 | 
					    [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)?',
 | 
					        'terrier', 'bull[ -]?terrier', 'australian[ -]?shepherd', 'australian[ -]?shep[h]?ard', 'german[ -]?shep[h]?([ea]rd)?',
 | 
				
			||||||
        'malinois', 'woof', 'labrador', 'collie', 'canis', 'lupus', 'canid', 'chihuahua', 'poodle', 'chinchilla',
 | 
					        'malinois', 'woof', 'labrador', 'collie', 'canis', 'lupus', 'canid', 'chihuahua', 'poodle', 'chinchilla',
 | 
				
			||||||
        'chow[ -]?chow', 'corgi', 'anubis', 'beagle', '.*wolf', 'direwolf', 'pointer', 'dhole',
 | 
					        'chow[ -]?chow', 'corgi', 'anubis', 'beagle', '.*wolf', 'direwolf', 'pointer', 'dhole', 'worg(en)?',
 | 
				
			||||||
        'anubian', 'dalmatian', 'dalmation', 'inumimi', 'lupine', 'malamute', 'mastiff', 'mutt', 'rottweill?er', 'shih[ -]?tzu', 'worgen',
 | 
					        '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',
 | 
					        'vallhund', 'puppy', 'oo?kami', 'great[ -]?dane', 'golden[ -]?(retriever|lab|labrador)', 'cocker[ -]?spaniel', 'samoyed', 'awoo',
 | 
				
			||||||
        'borzoi', 'spaniel', 'ookamimimi', 'jakkarumimi', 'chinchiramimi', 'woffo', 'wuff', 'wolfdog', 'setter', 'papillon',
 | 
					        'borzoi', 'spaniel', 'ookamimimi', 'jakkarumimi', 'chinchiramimi', 'woffo', 'wuff', 'wolfdog', 'setter', 'papillon',
 | 
				
			||||||
        '🐶', '🐺', '🐕', '🐩', 'aussie[ -]?doodle', 'shiba', 'inu', 'veil[ -]?hound', 'timber[ -]?wolf', 'hell[ -]?hound', 'hound',
 | 
					        '🐶', '🐺', '🐕', '🐩', 'aussie[ -]?doodle', 'shiba', 'inu', 'veil[ -]?hound', 'timber[ -]?wolf', 'hell[ -]?hound', 'hound',
 | 
				
			||||||
@ -296,10 +297,10 @@ export const speciesMapping: SpeciesMap = {
 | 
				
			|||||||
        gen('dragon')
 | 
					        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',
 | 
					        'gecko', 'reptile', 'reptilian', 'scaly', 'scale[ -]?born', 'argonian', 'saxhleel', 'skink', 'cobra', 'turtle', 'tortoise',
 | 
				
			||||||
        'nope[ -]rope', 'anaconda', 'python', gen('(lizard|snake)'), 'yuan[ -]?ti', 'crocodilian',
 | 
					        'nope[ -]rope', 'anaconda', 'python', gen('(lizard|snake|croc)'), 'yuan[ -]?ti', 'crocodilian',
 | 
				
			||||||
        'serp[ea]a?nt(ine)?', 'viper', 'titanoboa', 'boa', 'taipan',
 | 
					        'serp[ea]a?nt(ine)?', 'viper', 'titanoboa', 'boa', 'taipan', 'croc',
 | 
				
			||||||
        'ludroth', 'zvarr', '🐍'],
 | 
					        'ludroth', 'zvarr', '🐍'],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    [Species.Pokemon]: ['charizard', 'charmander', 'pikachu', 'digimon', 'renamon', 'eevee', 'gardev(oi|io)r', 'absol', 'aggron',
 | 
					    [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',
 | 
					    [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',
 | 
					        '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.*',
 | 
					    [Species.Caprinae]: ['sheep', 'goat', 'ibex', 'takin', 'bharal', 'goral', 'serow', 'lamb', 'faun', 'ram', 'faunus', 'goat.*',
 | 
				
			||||||
 | 
				
			|||||||
@ -806,7 +806,7 @@ export class Matcher {
 | 
				
			|||||||
        const s = Matcher.getMappedSpecies(mySpecies.string);
 | 
					        const s = Matcher.getMappedSpecies(mySpecies.string);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (!s) {
 | 
					        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;
 | 
					        return s;
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,7 @@
 | 
				
			|||||||
<template>
 | 
					<template>
 | 
				
			||||||
    <div id="character-page-sidebar" class="card bg-light">
 | 
					    <div id="character-page-sidebar" class="card bg-light">
 | 
				
			||||||
        <div class="card-body">
 | 
					        <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>
 | 
					            <div v-if="character.character.title" class="character-title">{{ character.character.title }}</div>
 | 
				
			||||||
            <character-action-menu :character="character" @rename="showRename()" @delete="showDelete()"
 | 
					            <character-action-menu :character="character" @rename="showRename()" @delete="showDelete()"
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user