Friendlier UX for image previews; profile kinks now inlined; character info sidebar cleaner; full size images on character images page
This commit is contained in:
parent
cb81610515
commit
af1960ed02
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<!-- hiding elements instead of using 'v-if' is used here as an optimization -->
|
||||
<div class="image-preview-wrapper" :style="{display: visible ? 'block' : 'none'}">
|
||||
<webview webpreferences="allowRunningInsecureContent" id="image-preview-ext" ref="imagePreviewExt" class="image-preview-external" :src="externalUrl" :style="{display: externalUrlVisible ? 'flex' : 'none'}"></webview>
|
||||
<webview webpreferences="allowRunningInsecureContent, autoplayPolicy=no-user-gesture-required" id="image-preview-ext" ref="imagePreviewExt" class="image-preview-external" :src="externalUrl" :style="{display: externalUrlVisible ? 'flex' : 'none'}"></webview>
|
||||
<div
|
||||
class="image-preview-local"
|
||||
:style="{backgroundImage: `url(${internalUrl})`, display: internalUrlVisible ? 'block' : 'none'}"
|
||||
|
@ -16,31 +16,40 @@
|
|||
import {EventBus} from './event-bus';
|
||||
import {domain} from '../bbcode/core';
|
||||
import {ImagePreviewMutator} from './image-preview-mutator';
|
||||
import {Point, screen} from 'electron';
|
||||
|
||||
@Component
|
||||
export default class ImagePreview extends Vue {
|
||||
private readonly MinTimePreviewVisible = 500;
|
||||
|
||||
public visible: boolean = false;
|
||||
|
||||
public externalUrlVisible: boolean = false;
|
||||
public internalUrlVisible: boolean = false;
|
||||
|
||||
public externalUrl: string|null = null;
|
||||
public internalUrl: string|null = null;
|
||||
public externalUrl: string | null = null;
|
||||
public internalUrl: string | null = null;
|
||||
|
||||
public url: string|null = null;
|
||||
public domain: string|undefined;
|
||||
public url: string | null = null;
|
||||
public domain: string | undefined;
|
||||
|
||||
private jsMutator = new ImagePreviewMutator();
|
||||
private interval: any = null;
|
||||
|
||||
private exitInterval: any = null;
|
||||
private exitUrl: string|null = null;
|
||||
private exitUrl: string | null = null;
|
||||
|
||||
private initialCursorPosition: Point | null = null;
|
||||
private shouldDismiss = false;
|
||||
private visibleSince = 0;
|
||||
|
||||
|
||||
@Hook('mounted')
|
||||
onMounted() {
|
||||
onMounted(): void {
|
||||
EventBus.$on(
|
||||
'imagepreview-dismiss',
|
||||
(eventData: any) => {
|
||||
// console.log('Event dismiss', eventData.url);
|
||||
this.dismiss(eventData.url);
|
||||
}
|
||||
);
|
||||
|
@ -48,6 +57,7 @@
|
|||
EventBus.$on(
|
||||
'imagepreview-show',
|
||||
(eventData: any) => {
|
||||
// console.log('Event show', eventData.url);
|
||||
this.show(eventData.url);
|
||||
}
|
||||
);
|
||||
|
@ -57,6 +67,8 @@
|
|||
webview.addEventListener(
|
||||
'dom-ready',
|
||||
() => {
|
||||
// webview.openDevTools();
|
||||
|
||||
const url = webview.getURL();
|
||||
|
||||
const js = this.jsMutator.getMutatorJsForSite(url);
|
||||
|
@ -64,67 +76,92 @@
|
|||
if (js) {
|
||||
webview.executeJavaScript(js);
|
||||
}
|
||||
|
||||
// webview.openDevTools();
|
||||
|
||||
/* webview.executeJavaScript(
|
||||
"(() => {"
|
||||
+ "$('#topbar').hide();"
|
||||
+ "$('.post-header').hide();"
|
||||
+ "$('#inside').css({padding: 0, margin: 0, width: '100%'});"
|
||||
+ "$('#right-content').hide();"
|
||||
+ "$('.post-container').css({width: '100%'});"
|
||||
+ "$('.post-image img').css({width: '100%', 'min-height': 'unset'});"
|
||||
+ "$('#recommendations').hide();"
|
||||
+ "$('.left').css({float: 'none'});"
|
||||
+ "})()"
|
||||
);*/
|
||||
}
|
||||
);
|
||||
|
||||
webview.getWebContents().on(
|
||||
'did-finish-load',
|
||||
()=> {
|
||||
webview.getWebContents().session.on(
|
||||
'will-download',
|
||||
(e: any) => {
|
||||
e.preventDefault();
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
setInterval(
|
||||
() => {
|
||||
if (((this.visible) && (!this.exitInterval) && (!this.shouldDismiss)) || (this.interval))
|
||||
this.initialCursorPosition = screen.getCursorScreenPoint();
|
||||
|
||||
if ((this.visible) && (this.shouldDismiss) && (this.hasMouseMovedSince()) && (!this.exitInterval) && (!this.interval))
|
||||
this.hide();
|
||||
},
|
||||
10
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
dismiss(url: string) {
|
||||
if (this.url !== url) {
|
||||
// simply ignore
|
||||
return;
|
||||
}
|
||||
private hide(): void {
|
||||
this.cancelExitTimer();
|
||||
|
||||
let due = this.visible ? 1000 : 0;
|
||||
this.url = null;
|
||||
this.visible = false;
|
||||
|
||||
this.internalUrlVisible = false;
|
||||
this.externalUrlVisible = false;
|
||||
|
||||
this.externalUrl = 'about:blank';
|
||||
this.internalUrl = 'about:blank';
|
||||
|
||||
this.exitUrl = null;
|
||||
this.exitInterval = null;
|
||||
|
||||
this.shouldDismiss = false;
|
||||
}
|
||||
|
||||
|
||||
dismiss(url: string): void {
|
||||
if (this.url !== url)
|
||||
return; // simply ignore
|
||||
|
||||
// console.log('DISMISS');
|
||||
|
||||
const due = this.visible ? this.MinTimePreviewVisible - Math.min(this.MinTimePreviewVisible, (Date.now() - this.visibleSince)) : 0;
|
||||
|
||||
this.cancelTimer();
|
||||
|
||||
if (this.exitInterval) {
|
||||
if (this.exitInterval)
|
||||
return;
|
||||
}
|
||||
|
||||
this.exitUrl = this.url;
|
||||
this.shouldDismiss = true;
|
||||
|
||||
if (!this.hasMouseMovedSince())
|
||||
return;
|
||||
|
||||
// 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
|
||||
// from the link
|
||||
this.exitInterval = setTimeout(
|
||||
() => {
|
||||
this.url = null;
|
||||
this.visible = false;
|
||||
|
||||
this.internalUrlVisible = false;
|
||||
this.externalUrlVisible = false;
|
||||
|
||||
this.externalUrl = 'about:blank';
|
||||
this.internalUrl = 'about:blank';
|
||||
|
||||
this.exitUrl = null;
|
||||
this.exitInterval = null;
|
||||
},
|
||||
() => this.hide(),
|
||||
due
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
show(url: string) {
|
||||
// url = 'https://imgur.com/a/2uzWx';
|
||||
// url = 'http://lodash.com';
|
||||
// url = 'https://rule34.xxx/index.php?page=post&s=view&id=3254983';
|
||||
show(url: string): void {
|
||||
// console.log('SHOW');
|
||||
|
||||
let due = ((url === this.exitUrl) && (this.exitInterval)) ? 0 : 100;
|
||||
if ((this.visible) && (!this.hasMouseMovedSince()))
|
||||
return;
|
||||
|
||||
if ((this.url === url) && ((this.visible) || (this.interval)))
|
||||
return;
|
||||
|
||||
const due = ((url === this.exitUrl) && (this.exitInterval)) ? 0 : 100;
|
||||
|
||||
this.url = url;
|
||||
this.domain = domain(url);
|
||||
|
@ -132,6 +169,8 @@
|
|||
this.cancelExitTimer();
|
||||
this.cancelTimer();
|
||||
|
||||
// This timer makes sure that just by accidentally brushing across a link won't show (blink) the preview
|
||||
// -- you actually have to pause on it
|
||||
this.interval = setTimeout(
|
||||
() => {
|
||||
const isInternal = this.isInternalUrl();
|
||||
|
@ -145,46 +184,56 @@
|
|||
this.externalUrl = this.url;
|
||||
|
||||
this.visible = true;
|
||||
this.visibleSince = Date.now();
|
||||
|
||||
this.initialCursorPosition = screen.getCursorScreenPoint();
|
||||
},
|
||||
due
|
||||
);
|
||||
}
|
||||
|
||||
hasMouseMovedSince(): boolean {
|
||||
if (!this.initialCursorPosition)
|
||||
return true;
|
||||
|
||||
cancelTimer() {
|
||||
if (this.interval) {
|
||||
clearTimeout(this.interval);
|
||||
try {
|
||||
const p = screen.getCursorScreenPoint();
|
||||
|
||||
return ((p.x !== this.initialCursorPosition.x) || (p.y !== this.initialCursorPosition.y));
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
cancelTimer(): void {
|
||||
if (this.interval)
|
||||
clearTimeout(this.interval);
|
||||
|
||||
this.interval = null;
|
||||
}
|
||||
|
||||
|
||||
cancelExitTimer() {
|
||||
if (this.exitInterval) {
|
||||
cancelExitTimer(): void {
|
||||
if (this.exitInterval)
|
||||
clearTimeout(this.exitInterval);
|
||||
}
|
||||
|
||||
this.exitInterval = null;
|
||||
}
|
||||
|
||||
|
||||
isVisible() {
|
||||
isVisible(): boolean {
|
||||
return this.visible;
|
||||
}
|
||||
|
||||
|
||||
getUrl() {
|
||||
getUrl(): string | null {
|
||||
return this.url;
|
||||
}
|
||||
|
||||
|
||||
isExternalUrl() {
|
||||
isExternalUrl(): boolean {
|
||||
return !((this.domain === 'f-list.net') || (this.domain === 'static.f-list.net'));
|
||||
}
|
||||
|
||||
|
||||
isInternalUrl() {
|
||||
isInternalUrl(): boolean {
|
||||
return !this.isExternalUrl();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
<template>
|
||||
<span
|
||||
@mouseover="show()"
|
||||
@mouseleave="dismiss()"
|
||||
>
|
||||
<span>
|
||||
<i class="fa fa-link"></i>
|
||||
<a
|
||||
:href="url"
|
||||
rel="nofollow noreferrer noopener"
|
||||
target="_blank"
|
||||
class="user-link"
|
||||
:title="url"
|
||||
@mouseover="show()"
|
||||
@mouseenter="show()"
|
||||
@mouseleave="dismiss()"
|
||||
@mouseout="dismiss()"
|
||||
>{{text}}</a>
|
||||
<span
|
||||
class="link-domain bbcode-pseudo"
|
||||
|
@ -34,24 +34,21 @@
|
|||
@Prop({required: true})
|
||||
readonly domain!: string;
|
||||
|
||||
@Prop()
|
||||
hover!: boolean = false;
|
||||
|
||||
@Hook("beforeDestroy")
|
||||
beforeDestroy() {
|
||||
beforeDestroy(): void {
|
||||
this.dismiss();
|
||||
}
|
||||
|
||||
@Hook("deactivated")
|
||||
deactivate() {
|
||||
deactivate(): void {
|
||||
this.dismiss();
|
||||
}
|
||||
|
||||
dismiss() {
|
||||
dismiss(): void {
|
||||
EventBus.$emit('imagepreview-dismiss', {url: this.url});
|
||||
}
|
||||
|
||||
show() {
|
||||
show(): void {
|
||||
EventBus.$emit('imagepreview-show', {url: this.url});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,17 +34,14 @@ export class ImagePreviewMutator {
|
|||
}
|
||||
|
||||
protected init() {
|
||||
this.add('e621.net', this.getBaseJsMutatorScript('#image'));
|
||||
this.add('e-hentai.org', this.getBaseJsMutatorScript('#img'));
|
||||
this.add('gelbooru.com', this.getBaseJsMutatorScript('#image'));
|
||||
this.add('chan.sankakucomplex.com', this.getBaseJsMutatorScript('#image'));
|
||||
this.add('e621.net', this.getBaseJsMutatorScript('#image, video'));
|
||||
this.add('e-hentai.org', this.getBaseJsMutatorScript('#img, video'));
|
||||
this.add('gelbooru.com', this.getBaseJsMutatorScript('#image, video'));
|
||||
this.add('chan.sankakucomplex.com', this.getBaseJsMutatorScript('#image, video'));
|
||||
this.add('gfycat.com', this.getBaseJsMutatorScript('video'));
|
||||
|
||||
this.add(
|
||||
'gfycat.com',
|
||||
`${this.getBaseJsMutatorScript('video')}
|
||||
document.querySelector('video').play();
|
||||
`
|
||||
);
|
||||
// this fixes videos only -- images are fine as is
|
||||
this.add('i.imgur.com', this.getBaseJsMutatorScript('video'));
|
||||
|
||||
this.add(
|
||||
'imgur.com',
|
||||
|
@ -56,25 +53,12 @@ export class ImagePreviewMutator {
|
|||
if(imageCount > 1)
|
||||
$('#flistWrapper').append('<div id="imageCount" style="position: absolute; bottom: 0; right: 0; background: green; border: 2px solid lightgreen; width: 5rem; height: 5rem; font-size: 2rem; font-weight: bold; color: white; border-radius: 5rem; margin: 0.75rem;"><div style="position: absolute; top: 50%; left: 50%; transform: translateY(-50%) translateX(-50%);">+' + (imageCount - 1) + '</div></div>');
|
||||
`
|
||||
|
||||
// "$('#topbar').hide();"
|
||||
// + "$('.post-header').hide();"
|
||||
// + "$('#inside').css({padding: 0, margin: 0, width: '100%'});"
|
||||
// + "$('#right-content').hide();"
|
||||
// + "$('.post-container').css({width: '100%'});"
|
||||
// + "$('.post-image img').css({width: 'auto', 'min-height': 'unset', 'max-height': '100vh'});"
|
||||
// + "$('#recommendations').hide();"
|
||||
// + "$('.left').css({float: 'none'});"
|
||||
// + "$('body').css({overflow: 'hidden'});"
|
||||
// + "const imageCount = $('.post-image-container').length;"
|
||||
// + "if(imageCount > 1) {"
|
||||
// + "$('body').append('<div id=\"imageCount\" style=\"position: absolute; bottom: 0; right: 0; background: green; border: 2px solid lightgreen; width: 5rem; height: 5rem; font-size: 2rem; font-weight: bold; color: white; border-radius: 5rem; margin: 0.75rem;\"><div style=\"position: absolute; top: 50%; left: 50%; transform: translateY(-50%) translateX(-50%);\">+' + (imageCount - 1) + '</div></div>');"
|
||||
// + "}"
|
||||
);
|
||||
|
||||
|
||||
this.add(
|
||||
'rule34.xxx',
|
||||
`${this.getBaseJsMutatorScript('#image')}
|
||||
`${this.getBaseJsMutatorScript('#image, video')}
|
||||
const content = document.querySelector('#content');
|
||||
content.remove();
|
||||
`
|
||||
|
@ -95,6 +79,14 @@ export class ImagePreviewMutator {
|
|||
body.append(el);
|
||||
body.style = 'padding: 0; margin: 0; overflow: hidden; width: 100%; height: 100%';
|
||||
img.style = 'object-position: top left; object-fit: contain; width: 100%; height: 100%;'
|
||||
|
||||
if (img.play) { img.muted = true; img.play(); }
|
||||
|
||||
let removeList = [];
|
||||
body.childNodes.forEach((el) => { if(el.id !== 'flistWrapper') { removeList.push(el); } });
|
||||
removeList.forEach((el) => el.remove());
|
||||
removeList = [];
|
||||
|
||||
`;
|
||||
}
|
||||
|
||||
|
|
|
@ -84,7 +84,10 @@
|
|||
|
||||
@Hook('mounted')
|
||||
mounted(): void {
|
||||
// browserWindow.webContents.openDevTools();
|
||||
|
||||
this.addTab();
|
||||
|
||||
electron.ipcRenderer.on('settings', (_: Event, settings: GeneralSettings) => this.settings = settings);
|
||||
electron.ipcRenderer.on('allow-new-tabs', (_: Event, allow: boolean) => this.canOpenTab = allow);
|
||||
electron.ipcRenderer.on('open-tab', () => this.addTab());
|
||||
|
|
|
@ -142,6 +142,7 @@ function createWindow(): Electron.BrowserWindow | undefined {
|
|||
window.show();
|
||||
if(lastState.maximized) window.maximize();
|
||||
});
|
||||
|
||||
return window;
|
||||
}
|
||||
|
||||
|
|
25
readme.md
25
readme.md
|
@ -1,3 +1,28 @@
|
|||
# F-Chat Rising
|
||||
|
||||
This repository contains a modified version of the mainline F-Chat 3.0 client.
|
||||
|
||||
|
||||
## Key Differences
|
||||
|
||||
* Ad auto-posting
|
||||
* Manage channel's ad settings in "Tab Settings"
|
||||
* Automatically repost ads every 11-18 minutes (randomized)
|
||||
* Auto-posting can rotate through multiple ads
|
||||
* Link previews
|
||||
* Hover cursor over any `[url]` to see a preview of it
|
||||
* Profile
|
||||
* Kinks are auto-compared when profile is loaded
|
||||
* Custom kink explanations are shown inline
|
||||
* Custom kinks are highlighted
|
||||
* Gender, fur/human status, age, and sexual preference are highlighted if compatible or incompatible
|
||||
* Guestbook, friend, and group counts are visible on tabs
|
||||
* Character pictures can be expanded inline
|
||||
* Cleaner presentation for the side bar details (age, etc.), sorted in most relevant order
|
||||
* Less informative side bar details (views, contact) are separated and shown in a less prominent way
|
||||
|
||||
|
||||
|
||||
# F-List Exported
|
||||
This repository contains the open source parts of F-list and F-Chat 3.0.
|
||||
All necessary files to build F-Chat 3.0 as an Electron, mobile or web application are included.
|
||||
|
|
|
@ -166,4 +166,86 @@
|
|||
this.loading = false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</script>
|
||||
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
.custom-kink {
|
||||
&:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
font-weight: bold;
|
||||
margin-top: 14px;
|
||||
margin-bottom: 14px;
|
||||
margin-left: -6px;
|
||||
margin-right: -6px;
|
||||
color: #f2cd00;
|
||||
border: 1px rgba(255, 255, 255, 0.1) solid;
|
||||
border-radius: 2px;
|
||||
/* border-collapse: collapse; */
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
|
||||
.kink-custom-desc {
|
||||
display: block;
|
||||
font-weight: normal;
|
||||
font-size: 0.9rem;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
line-height: 125%;
|
||||
}
|
||||
|
||||
|
||||
.infotag-label {
|
||||
display: block;
|
||||
/* margin-bottom: 1rem; */
|
||||
font-weight: normal !important;
|
||||
line-height: 120%;
|
||||
font-size: 85%;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
}
|
||||
|
||||
|
||||
.infotag-value {
|
||||
display: block;
|
||||
margin-bottom: 1rem;
|
||||
font-weight: bold;
|
||||
line-height: 120%;
|
||||
}
|
||||
|
||||
.quick-info-value {
|
||||
display: block;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.quick-info-label {
|
||||
display: block;
|
||||
/* margin-bottom: 1rem; */
|
||||
font-weight: normal !important;
|
||||
line-height: 120%;
|
||||
font-size: 85%;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
}
|
||||
|
||||
.quick-info {
|
||||
margin-bottom: 1rem;
|
||||
font-size: 0.9rem;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
}
|
||||
|
||||
img.character-image {
|
||||
max-width: 33% !important;
|
||||
width: 33% !important;
|
||||
height: auto !important;
|
||||
object-fit: contain;
|
||||
object-position: top center;
|
||||
vertical-align: top !important;
|
||||
}
|
||||
|
||||
</style>
|
|
@ -1,12 +1,19 @@
|
|||
<template>
|
||||
<div class="character-images row">
|
||||
<!-- <div class="character-images row">-->
|
||||
<div class="character-images">
|
||||
<div v-show="loading" class="alert alert-info">Loading images.</div>
|
||||
<template v-if="!loading">
|
||||
<div class="character-image col-6 col-sm-4 col-md-2" v-for="image in images" :key="image.id">
|
||||
<a :href="imageUrl(image)" target="_blank" @click="handleImageClick($event, image)">
|
||||
<img :src="thumbUrl(image)" :title="image.description">
|
||||
</a>
|
||||
</div>
|
||||
<img :src="imageUrl(image)" :title="image.description" class="character-image" v-for="image in images" :key="image.id">
|
||||
|
||||
<!-- <div class="character-image col-6 col-sm-12 col-md-12" v-for="image in images" :key="image.id">-->
|
||||
<!-- <img :src="imageUrl(image)" :title="image.description">-->
|
||||
<!-- </div>-->
|
||||
<!-- -->
|
||||
|
||||
<!-- <a :href="imageUrl(image)" target="_blank" @click="handleImageClick($event, image)">-->
|
||||
<!-- <img :src="thumbUrl(image)" :title="image.description">-->
|
||||
<!-- </a>-->
|
||||
<!-- </div>-->
|
||||
</template>
|
||||
<div v-if="!loading && !images.length" class="alert alert-info">No images.</div>
|
||||
<div class="image-preview" v-show="previewImage" @click="previewImage = ''">
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<div class="infotag">
|
||||
<span class="infotag-label">{{label}}: </span>
|
||||
<span class="infotag-label">{{label}}</span>
|
||||
<span v-if="!contactLink" class="infotag-value">{{value}}</span>
|
||||
<span v-if="contactLink" class="infotag-value"><a :href="contactLink">{{value}}</a></span>
|
||||
</div>
|
||||
|
|
|
@ -2,15 +2,16 @@
|
|||
<div class="character-kink" :class="kinkClasses" :id="kinkId" @click="toggleSubkinks" :data-custom="customId"
|
||||
@mouseover.stop="showTooltip = true" @mouseout.stop="showTooltip = false">
|
||||
<i v-show="kink.hasSubkinks" class="fa" :class="{'fa-minus': !listClosed, 'fa-plus': listClosed}"></i>
|
||||
<i v-show="!kink.hasSubkinks && kink.isCustom" class="far fa-dot-circle custom-kink-icon"></i>
|
||||
<i v-show="!kink.hasSubkinks && kink.isCustom" class="far custom-kink-icon"></i>
|
||||
<span class="kink-name">{{ kink.name }}</span>
|
||||
<span class="kink-custom-desc" v-if="(kink.isCustom)">{{kink.description}}</span>
|
||||
<template v-if="kink.hasSubkinks">
|
||||
<div class="subkink-list" :class="{closed: this.listClosed}">
|
||||
<kink v-for="subkink in kink.subkinks" :kink="subkink" :key="subkink.id" :comparisons="comparisons"
|
||||
:highlights="highlights"></kink>
|
||||
</div>
|
||||
</template>
|
||||
<div class="popover popover-top" v-if="showTooltip" style="display:block;bottom:100%;top:initial;margin-bottom:5px">
|
||||
<div class="popover popover-top" v-if="((showTooltip) && (!kink.isCustom))" style="display:block;bottom:100%;top:initial;margin-bottom:5px">
|
||||
<div class="arrow" style="left:10%"></div>
|
||||
<h5 class="popover-header">{{kink.name}}</h5>
|
||||
<div class="popover-body"><p>{{kink.description}}</p></div>
|
||||
|
|
|
@ -129,19 +129,16 @@
|
|||
this.highlighting = toAssign;
|
||||
}
|
||||
|
||||
|
||||
@Hook('mounted')
|
||||
async mounted(): Promise<void> {
|
||||
await this.compareKinks();
|
||||
}
|
||||
|
||||
|
||||
@Watch('character')
|
||||
characterChanged(): void {
|
||||
this.compareKinks();
|
||||
async characterChanged(): Promise<void> {
|
||||
await this.compareKinks();
|
||||
}
|
||||
|
||||
|
||||
get kinkGroups(): {[key: string]: KinkGroup | undefined} {
|
||||
return this.shared.kinks.kink_groups;
|
||||
}
|
||||
|
|
|
@ -45,23 +45,23 @@
|
|||
<div class="quick-info-block">
|
||||
<infotag-item v-for="infotag in quickInfoItems" :infotag="infotag" :key="infotag.id"></infotag-item>
|
||||
<div class="quick-info">
|
||||
<span class="quick-info-label">Created: </span>
|
||||
<span class="quick-info-label">Created</span>
|
||||
<span class="quick-info-value"><date :time="character.character.created_at"></date></span>
|
||||
</div>
|
||||
<div class="quick-info">
|
||||
<span class="quick-info-label">Last updated: </span>
|
||||
<span class="quick-info-label">Last Updated </span>
|
||||
<span class="quick-info-value"><date :time="character.character.updated_at"></date></span>
|
||||
</div>
|
||||
<div class="quick-info" v-if="character.character.last_online_at">
|
||||
<span class="quick-info-label">Last online:</span>
|
||||
<span class="quick-info-label">Last Online</span>
|
||||
<span class="quick-info-value"><date :time="character.character.last_online_at"></date></span>
|
||||
</div>
|
||||
<div class="quick-info">
|
||||
<span class="quick-info-label">Views: </span>
|
||||
<span class="quick-info-label">Views</span>
|
||||
<span class="quick-info-value">{{character.character.views}}</span>
|
||||
</div>
|
||||
<div class="quick-info" v-if="character.character.timezone != null">
|
||||
<span class="quick-info-label">Timezone:</span>
|
||||
<span class="quick-info-label">Timezone</span>
|
||||
<span class="quick-info-value">
|
||||
UTC{{character.character.timezone > 0 ? '+' : ''}}{{character.character.timezone != 0 ? character.character.timezone : ''}}
|
||||
</span>
|
||||
|
|
Loading…
Reference in New Issue