<!-- 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()" @click.middle.prevent.stop="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><span v-if="!!smartFilterIcon" :class="smartFilterIcon"></span>{{character.name}}<span v-if="!!matchClass" :class="matchClass">{{getMatchScoreTitle(matchScore)}}</span></span></template> <script lang="ts"> import { Component, Hook, Prop, Watch } from '@f-list/vue-ts'; import Vue from 'vue'; import {Channel, Character} from '../fchat'; import { Score } from '../learn/matcher'; import core from './core'; import { EventBus } from './preview/event-bus'; import { kinkMatchWeights, Scoring } from '../learn/matcher-types'; export function getStatusIcon(status: Character.Status): string { switch(status) { case 'online': return 'far fa-user'; case 'looking': return 'fa fa-eye'; case 'dnd': return 'fa fa-minus-circle'; case 'offline': return 'fa fa-ban'; case 'away': return 'far fa-circle'; case 'busy': return 'fa fa-cog'; case 'idle': return 'far fa-clock'; case 'crown': return 'fa fa-birthday-cake'; } } export interface StatusClasses { rankIcon: string | null; smartFilterIcon: string | null; statusClass: string | null; matchClass: string | null; matchScore: number | string | null; userClass: string; isBookmark: boolean; } export function getStatusClasses( character: Character, channel: Channel | undefined, showStatus: boolean, showBookmark: boolean, showMatch: boolean ): StatusClasses { let rankIcon: string | null = null; let statusClass = null; let matchClass = null; let matchScore = null; let smartFilterIcon: string | null = null; if(character.isChatOp) { rankIcon = 'far fa-gem'; } else if(channel !== undefined) { rankIcon = (channel.owner === character.name) ? 'fa fa-key' : channel.opList.indexOf(character.name) !== -1 ? (channel.id.substr(0, 4) === 'adh-' ? 'fa fa-shield-alt' : 'fa fa-star') : null; } if ((showStatus) || (character.status === 'crown')) statusClass = `fa-fw ${getStatusIcon(character.status)}`; const cache = ((showMatch) && ((core.state.settings.risingAdScore) || (core.state.settings.risingFilter.showFilterIcon))) ? core.cache.profileCache.getSync(character.name) : undefined; // undefined == not interested // null == no cache hit if (cache === null) { void core.cache.addProfile(character.name); } if ((core.state.settings.risingAdScore) && (showMatch) && (cache)) { if ((cache.match.searchScore >= kinkMatchWeights.unicornThreshold) && (cache.match.matchScore === Scoring.MATCH)) { matchClass = 'match-found unicorn'; matchScore = 'unicorn'; } else { matchClass = `match-found ${Score.getClasses(cache.match.matchScore)}`; matchScore = cache.match.matchScore; } } if (core.state.settings.risingFilter.showFilterIcon && cache?.match.isFiltered) { smartFilterIcon = 'user-filter fas fa-filter'; } const gender = character.gender !== undefined ? character.gender.toLowerCase() : 'none'; const isBookmark = (showBookmark) && (core.connection.isOpen) && (core.state.settings.colorBookmarks) && ((character.isFriend) || (character.isBookmarked)); const userClass = `user-view gender-${gender}${isBookmark ? ' user-bookmark' : ''}`; return { rankIcon: rankIcon ? `user-rank ${rankIcon}` : null, statusClass: statusClass ? `user-status ${statusClass}` : null, matchClass, matchScore, userClass, smartFilterIcon, isBookmark }; } @Component({ components: { } }) export default class UserView extends Vue { @Prop({required: true}) readonly character!: Character; @Prop() readonly channel?: Channel; @Prop() readonly showStatus?: boolean = true; @Prop({default: true}) readonly bookmark?: boolean = true; @Prop() readonly match?: boolean = false; @Prop({default: true}) readonly preview: boolean = true; userClass = ''; rankIcon: string | null = null; smartFilterIcon: string | null = null; statusClass: string | null = null; matchClass: string | null = null; matchScore: number | string | null = null; // tslint:disable-next-line no-any scoreWatcher: ((event: any) => void) | null = null; @Hook('mounted') onMounted(): void { this.update(); if ((this.match) && (!this.matchClass)) { if (this.scoreWatcher) { EventBus.$off('character-score', this.scoreWatcher); } // tslint:disable-next-line no-unsafe-any no-any this.scoreWatcher = (event: any): void => { // console.log('scoreWatcher', event); // tslint:disable-next-line no-unsafe-any no-any if ((event.character) && (event.character.character.name === this.character.name)) { this.update(); if (this.scoreWatcher) { EventBus.$off('character-score', this.scoreWatcher); this.scoreWatcher = null; } } }; EventBus.$on( 'character-score', this.scoreWatcher ); } } @Hook('beforeDestroy') onBeforeDestroy(): void { if (this.scoreWatcher) EventBus.$off('character-score', this.scoreWatcher); this.dismiss(); } @Hook('deactivated') deactivate(): void { this.dismiss(); } @Hook('beforeUpdate') onBeforeUpdate(): void { this.update(); } @Watch('character.status') onStatusUpdate(): void { this.update(); } update(): void { // console.log('user.view.update', this.character.name); const res = getStatusClasses(this.character, this.channel, !!this.showStatus, !!this.bookmark, !!this.match); this.rankIcon = res.rankIcon; this.smartFilterIcon = res.smartFilterIcon; this.statusClass = res.statusClass; this.matchClass = res.matchClass; this.matchScore = res.matchScore; this.userClass = res.userClass; } getMatchScoreTitle(score: number | string | null): string { switch (score) { case 'unicorn': return 'Unicorn'; case Scoring.MATCH: return 'Great'; case Scoring.WEAK_MATCH: return 'Good'; case Scoring.WEAK_MISMATCH: return 'Maybe'; case Scoring.MISMATCH: return 'No'; } return ''; } getCharacterUrl(): string { return `flist-character://${this.character.name}`; } dismiss(force: boolean = false): void { if (!this.preview) { return; } EventBus.$emit('imagepreview-dismiss', {url: this.getCharacterUrl(), force}); } show(): void { if (!this.preview) { return; } EventBus.$emit('imagepreview-show', {url: this.getCharacterUrl()}); } toggleStickyness(): void { if (!this.preview) { return; } EventBus.$emit('imagepreview-toggle-stickyness', {url: this.getCharacterUrl()}); } } </script>