mirror of
https://github.com/FrankerFaceZ/FrankerFaceZ.git
synced 2025-08-04 11:44:00 +00:00
4.55.2
* Fixed: Do not set `src` for images on Firefox, since that causes animations to reset when a new image is added to the DOM with the same `src`. For some reason. Thanks, Firefox. * Fixed: The FFZ Control Center, Emote Cards, and Link Cards are now all aware of each other in a limited sense. The most recently interacted-with window will appear on top of the others. * Fixed: Channel page links not having enhanced tool-tip preview support. (Still no link cards when you click them, but it's a start.) * Developer: Made it so the Link Tester in Debugging > Data Sources will properly open link cards, as long as you have link cards enabled. * API Added: Update to the latest FFZ rich tokens format, which has a new `i18n_select` token. This allows, for example, the link preview service to return multiple languages of titles and descriptions for a YouTube video so that your client can pick the best one to display. * Experiment Changed: The "API-Based Link Lookups" experiment now has a third option that uses Cloudflare Workers. It's set very low while I try to gauge how much traffic it would see, and if this is a viable option.
This commit is contained in:
parent
29419eee22
commit
5b65f6b735
19 changed files with 223 additions and 91 deletions
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "frankerfacez",
|
"name": "frankerfacez",
|
||||||
"author": "Dan Salvato LLC",
|
"author": "Dan Salvato LLC",
|
||||||
"version": "4.55.1",
|
"version": "4.55.2",
|
||||||
"description": "FrankerFaceZ is a Twitch enhancement suite.",
|
"description": "FrankerFaceZ is a Twitch enhancement suite.",
|
||||||
"private": true,
|
"private": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
|
|
|
@ -12,15 +12,16 @@
|
||||||
"description": "Use the new API to look up links instead of the socket cluster.",
|
"description": "Use the new API to look up links instead of the socket cluster.",
|
||||||
"groups": [
|
"groups": [
|
||||||
{"value": true, "weight": 30},
|
{"value": true, "weight": 30},
|
||||||
{"value": false, "weight": 70}
|
{"value": "cf", "weight": 5},
|
||||||
|
{"value": false, "weight": 65}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"pubsub": {
|
"pubsub": {
|
||||||
"name": "MQTT-Based PubSub",
|
"name": "MQTT-Based PubSub",
|
||||||
"description": "An experimental new pubsub system that should be more reliable than the existing socket cluster.",
|
"description": "An experimental new pubsub system that should be more reliable than the existing socket cluster.",
|
||||||
"groups": [
|
"groups": [
|
||||||
{"value": true, "weight": 25},
|
{"value": true, "weight": 15},
|
||||||
{"value": false, "weight": 75}
|
{"value": false, "weight": 85}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -25,6 +25,7 @@ import * as RICH_PROVIDERS from './rich_providers';
|
||||||
import * as LINK_PROVIDERS from './link_providers';
|
import * as LINK_PROVIDERS from './link_providers';
|
||||||
|
|
||||||
import Actions from './actions/actions';
|
import Actions from './actions/actions';
|
||||||
|
import { LINK_DATA_HOSTS } from 'src/utilities/constants';
|
||||||
|
|
||||||
|
|
||||||
function sortPriorityColorTerms(list) {
|
function sortPriorityColorTerms(list) {
|
||||||
|
@ -131,6 +132,20 @@ export default class Chat extends Module {
|
||||||
});*/
|
});*/
|
||||||
|
|
||||||
this.settings.add('debug.link-resolver.source', {
|
this.settings.add('debug.link-resolver.source', {
|
||||||
|
process(ctx, val) {
|
||||||
|
if ( val == null ) {
|
||||||
|
const exp = this.experiments.getAssignment('api_links');
|
||||||
|
if ( exp === 'cf' )
|
||||||
|
val = 'test-cf';
|
||||||
|
else if ( exp )
|
||||||
|
val = 'test';
|
||||||
|
else
|
||||||
|
val = 'socket';
|
||||||
|
}
|
||||||
|
|
||||||
|
return LINK_DATA_HOSTS[val] ?? LINK_DATA_HOSTS.test;
|
||||||
|
},
|
||||||
|
|
||||||
default: null,
|
default: null,
|
||||||
ui: {
|
ui: {
|
||||||
path: 'Debugging > Data Sources >> Links',
|
path: 'Debugging > Data Sources >> Links',
|
||||||
|
@ -139,11 +154,10 @@ export default class Chat extends Module {
|
||||||
force_seen: true,
|
force_seen: true,
|
||||||
data: [
|
data: [
|
||||||
{value: null, title: 'Automatic'},
|
{value: null, title: 'Automatic'},
|
||||||
{value: 'dev', title: 'localhost'},
|
].concat(Object.entries(LINK_DATA_HOSTS).map(x => ({
|
||||||
{value: 'test', title: 'API Test'},
|
value: x[0],
|
||||||
{value: 'prod', title: 'API Production' },
|
title: x[1].title
|
||||||
{value: 'socket', title: 'Socket Cluster (Deprecated)'}
|
})))
|
||||||
]
|
|
||||||
},
|
},
|
||||||
|
|
||||||
changed: () => this.clearLinkCache()
|
changed: () => this.clearLinkCache()
|
||||||
|
@ -2307,23 +2321,16 @@ export default class Chat extends Module {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let provider = this.settings.get('debug.link-resolver.source');
|
let provider = this.settings.get('debug.link-resolver.source').value;
|
||||||
if ( provider == null )
|
if ( provider === 'special:socket' && ! this.socket )
|
||||||
provider = this.experiments.getAssignment('api_links') ? 'test' : 'socket';
|
provider = LINK_DATA_HOSTS.test.value;
|
||||||
|
|
||||||
if ( provider === 'socket' && ! this.socket )
|
if ( provider === 'special:socket' ) {
|
||||||
provider = 'test';
|
|
||||||
|
|
||||||
if ( provider === 'socket' ) {
|
|
||||||
timeout(this.socket.call('get_link', url), 15000)
|
timeout(this.socket.call('get_link', url), 15000)
|
||||||
.then(data => handle(true, data))
|
.then(data => handle(true, data))
|
||||||
.catch(err => handle(false, err));
|
.catch(err => handle(false, err));
|
||||||
} else {
|
} else {
|
||||||
const host = provider === 'dev' ? 'https://localhost:8002/' :
|
timeout(fetch(`${provider}?url=${encodeURIComponent(url)}`).then(r => r.json()), 15000)
|
||||||
provider === 'test' ? 'https://api-test.frankerfacez.com/v2/link' :
|
|
||||||
'https://api.frankerfacez.com/v2/link';
|
|
||||||
|
|
||||||
timeout(fetch(`${host}?url=${encodeURIComponent(url)}`).then(r => r.json()), 15000)
|
|
||||||
.then(data => handle(true, data))
|
.then(data => handle(true, data))
|
||||||
.catch(err => handle(false, err));
|
.catch(err => handle(false, err));
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ import {sanitize, createElement} from 'utilities/dom';
|
||||||
import {has, getTwitchEmoteURL, split_chars, getTwitchEmoteSrcSet} from 'utilities/object';
|
import {has, getTwitchEmoteURL, split_chars, getTwitchEmoteSrcSet} from 'utilities/object';
|
||||||
import { NoContent } from 'utilities/tooltip';
|
import { NoContent } from 'utilities/tooltip';
|
||||||
|
|
||||||
import {EmoteTypes, REPLACEMENT_BASE, REPLACEMENTS, WEIRD_EMOTE_SIZES} from 'utilities/constants';
|
import {EmoteTypes, IS_FIREFOX, REPLACEMENT_BASE, REPLACEMENTS, WEIRD_EMOTE_SIZES} from 'utilities/constants';
|
||||||
import {CATEGORIES, JOINER_REPLACEMENT} from './emoji';
|
import {CATEGORIES, JOINER_REPLACEMENT} from './emoji';
|
||||||
|
|
||||||
import { MODIFIER_FLAGS } from './emotes';
|
import { MODIFIER_FLAGS } from './emotes';
|
||||||
|
@ -1230,7 +1230,7 @@ const render_emote = (token, createElement, wrapped) => {
|
||||||
emote = createElement('img', {
|
emote = createElement('img', {
|
||||||
class: `${EMOTE_CLASS} ffz-tooltip${hoverSrc ? ' ffz-hover-emote' : ''}${token.provider === 'twitch' ? ' twitch-emote' : token.provider === 'ffz' ? ' ffz-emote' : token.provider === 'emoji' ? ' ffz-emoji' : ''}`,
|
class: `${EMOTE_CLASS} ffz-tooltip${hoverSrc ? ' ffz-hover-emote' : ''}${token.provider === 'twitch' ? ' twitch-emote' : token.provider === 'ffz' ? ' ffz-emote' : token.provider === 'emoji' ? ' ffz-emoji' : ''}`,
|
||||||
attrs: {
|
attrs: {
|
||||||
src,
|
src: IS_FIREFOX ? undefined : src,
|
||||||
srcSet,
|
srcSet,
|
||||||
alt: token.text,
|
alt: token.text,
|
||||||
height: (token.big && ! token.can_big && token.height) ? `${token.height * 2}px` : undefined,
|
height: (token.big && ! token.can_big && token.height) ? `${token.height * 2}px` : undefined,
|
||||||
|
@ -1429,7 +1429,7 @@ export const AddonEmotes = {
|
||||||
else
|
else
|
||||||
emote = (<img
|
emote = (<img
|
||||||
class={`${EMOTE_CLASS} ffz--pointer-events ffz-tooltip${hoverSrc ? ' ffz-hover-emote' : ''}${token.provider === 'twitch' ? ' twitch-emote' : token.provider === 'ffz' ? ' ffz-emote' : token.provider === 'emoji' ? ' ffz-emoji' : ''}`}
|
class={`${EMOTE_CLASS} ffz--pointer-events ffz-tooltip${hoverSrc ? ' ffz-hover-emote' : ''}${token.provider === 'twitch' ? ' twitch-emote' : token.provider === 'ffz' ? ' ffz-emote' : token.provider === 'emoji' ? ' ffz-emoji' : ''}`}
|
||||||
src={src}
|
src={IS_FIREFOX ? undefined : src}
|
||||||
srcSet={srcSet}
|
srcSet={srcSet}
|
||||||
style={style}
|
style={style}
|
||||||
height={style ? undefined : is_big ? `${token.height * 2}px` : undefined}
|
height={style ? undefined : is_big ? `${token.height * 2}px` : undefined}
|
||||||
|
|
|
@ -413,6 +413,8 @@ export default {
|
||||||
handle: this.$el.querySelector('.ffz-viewer-card__header'),
|
handle: this.$el.querySelector('.ffz-viewer-card__header'),
|
||||||
highlightInputs: true,
|
highlightInputs: true,
|
||||||
constrain: true,
|
constrain: true,
|
||||||
|
onMouseDown: () => this.onFocus(),
|
||||||
|
onTouchStart: () => this.onFocus(),
|
||||||
ignoreFn: e => e.target.closest('.viewer-card-drag-cancel') != null
|
ignoreFn: e => e.target.closest('.viewer-card-drag-cancel') != null
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
import {createElement} from 'utilities/dom';
|
import {createElement} from 'utilities/dom';
|
||||||
|
import { getDialogNextZ } from 'utilities/dialog';
|
||||||
import {deep_copy, getTwitchEmoteURL} from 'utilities/object';
|
import {deep_copy, getTwitchEmoteURL} from 'utilities/object';
|
||||||
import { EmoteTypes, TWITCH_GLOBAL_SETS, TWITCH_POINTS_SETS, TWITCH_PRIME_SETS } from 'utilities/constants';
|
import { EmoteTypes, TWITCH_GLOBAL_SETS, TWITCH_POINTS_SETS, TWITCH_PRIME_SETS } from 'utilities/constants';
|
||||||
|
|
||||||
|
@ -61,7 +62,7 @@ export default class EmoteCard extends Module {
|
||||||
|
|
||||||
this.vue = this.resolve('vue');
|
this.vue = this.resolve('vue');
|
||||||
|
|
||||||
this.last_z = 9000;
|
//this.last_z = 9000;
|
||||||
this.open_cards = {};
|
this.open_cards = {};
|
||||||
this.last_card = null;
|
this.last_card = null;
|
||||||
}
|
}
|
||||||
|
@ -463,7 +464,7 @@ export default class EmoteCard extends Module {
|
||||||
old_card = this.open_cards[card_key];
|
old_card = this.open_cards[card_key];
|
||||||
|
|
||||||
if ( old_card ) {
|
if ( old_card ) {
|
||||||
old_card.$el.style.zIndex = ++this.last_z;
|
old_card.$el.style.zIndex = getDialogNextZ();
|
||||||
old_card.focus();
|
old_card.focus();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -512,7 +513,7 @@ export default class EmoteCard extends Module {
|
||||||
|
|
||||||
getFFZ: () => this,
|
getFFZ: () => this,
|
||||||
reportTwitchEmote: (...args) => this.reportTwitchEmote(...args),
|
reportTwitchEmote: (...args) => this.reportTwitchEmote(...args),
|
||||||
getZ: () => ++this.last_z
|
getZ: getDialogNextZ
|
||||||
},
|
},
|
||||||
|
|
||||||
on: {
|
on: {
|
||||||
|
|
|
@ -490,6 +490,8 @@ export default {
|
||||||
handle: this.$el.querySelector('.ffz-viewer-card__header'),
|
handle: this.$el.querySelector('.ffz-viewer-card__header'),
|
||||||
highlightInputs: true,
|
highlightInputs: true,
|
||||||
constrain: true,
|
constrain: true,
|
||||||
|
onMouseDown: () => this.onFocus(),
|
||||||
|
onTouchStart: () => this.onFocus(),
|
||||||
ignoreFn: e => e.target.closest('.viewer-card-drag-cancel') != null
|
ignoreFn: e => e.target.closest('.viewer-card-drag-cancel') != null
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
import { createElement } from 'utilities/dom';
|
import { createElement } from 'utilities/dom';
|
||||||
|
import { getDialogNextZ } from 'utilities/dialog';
|
||||||
import { deep_copy } from 'utilities/object';
|
import { deep_copy } from 'utilities/object';
|
||||||
|
|
||||||
import Module from 'utilities/module';
|
import Module from 'utilities/module';
|
||||||
|
@ -44,7 +45,7 @@ export default class LinkCard extends Module {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.last_z = 9000;
|
//this.last_z = 9000;
|
||||||
this.open_cards = {};
|
this.open_cards = {};
|
||||||
this.last_card = null;
|
this.last_card = null;
|
||||||
}
|
}
|
||||||
|
@ -106,7 +107,7 @@ export default class LinkCard extends Module {
|
||||||
old_card = this.open_cards[card_key];
|
old_card = this.open_cards[card_key];
|
||||||
|
|
||||||
if ( old_card ) {
|
if ( old_card ) {
|
||||||
old_card.$el.style.zIndex = ++this.last_z;
|
old_card.$el.style.zIndex = getDialogNextZ();
|
||||||
old_card.focus();
|
old_card.focus();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -154,7 +155,7 @@ export default class LinkCard extends Module {
|
||||||
use_dest: this.settings.get('link-cards.use-destination'),
|
use_dest: this.settings.get('link-cards.use-destination'),
|
||||||
|
|
||||||
getFFZ: () => this,
|
getFFZ: () => this,
|
||||||
getZ: () => ++this.last_z
|
getZ: getDialogNextZ
|
||||||
},
|
},
|
||||||
|
|
||||||
on: {
|
on: {
|
||||||
|
|
|
@ -138,6 +138,7 @@
|
||||||
data-is-mail="false"
|
data-is-mail="false"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
|
@click="handleClick"
|
||||||
>
|
>
|
||||||
{{ decodeURI(url) }}
|
{{ decodeURI(url) }}
|
||||||
</a>
|
</a>
|
||||||
|
@ -222,6 +223,7 @@
|
||||||
|
|
||||||
import { debounce, timeout, pick_random } from 'utilities/object'
|
import { debounce, timeout, pick_random } from 'utilities/object'
|
||||||
import { highlightJson } from 'utilities/dom';
|
import { highlightJson } from 'utilities/dom';
|
||||||
|
import { LINK_DATA_HOSTS } from 'src/utilities/constants';
|
||||||
|
|
||||||
const STOCK_URLS = [
|
const STOCK_URLS = [
|
||||||
'https://www.twitch.tv/sirstendec',
|
'https://www.twitch.tv/sirstendec',
|
||||||
|
@ -274,6 +276,7 @@ export default {
|
||||||
force_tooltip: state?.ffz_lt_tip ?? false,
|
force_tooltip: state?.ffz_lt_tip ?? false,
|
||||||
|
|
||||||
events: {
|
events: {
|
||||||
|
emit: (...args) => this.item.getChat().emit(...args),
|
||||||
on: (...args) => this.item.getChat().on(...args),
|
on: (...args) => this.item.getChat().on(...args),
|
||||||
off: (...args) => this.item.getChat().off(...args)
|
off: (...args) => this.item.getChat().off(...args)
|
||||||
}
|
}
|
||||||
|
@ -384,6 +387,10 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
handleClick(event) {
|
||||||
|
return this.chat.handleLinkClick(event);
|
||||||
|
},
|
||||||
|
|
||||||
changeProvider() {
|
changeProvider() {
|
||||||
this.updateEventSource();
|
this.updateEventSource();
|
||||||
this.updateExamples();
|
this.updateExamples();
|
||||||
|
@ -391,7 +398,7 @@ export default {
|
||||||
|
|
||||||
updateEventSource() {
|
updateEventSource() {
|
||||||
const provider = this.settings.get('debug.link-resolver.source');
|
const provider = this.settings.get('debug.link-resolver.source');
|
||||||
if ( provider !== 'dev' ) {
|
if ( ! provider.has_sse ) {
|
||||||
if ( this.es ) {
|
if ( this.es ) {
|
||||||
this.es.close();
|
this.es.close();
|
||||||
this.es = null;
|
this.es = null;
|
||||||
|
@ -404,7 +411,7 @@ export default {
|
||||||
if ( this.es )
|
if ( this.es )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this.es = new EventSource('https://localhost:8002/sse');
|
this.es = new EventSource(`${provider.value}/sse`);
|
||||||
this.es.addEventListener('error', () => {
|
this.es.addEventListener('error', () => {
|
||||||
this.es_waiting = true;
|
this.es_waiting = true;
|
||||||
});
|
});
|
||||||
|
@ -467,23 +474,16 @@ export default {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this.examples_loading = true;
|
this.examples_loading = true;
|
||||||
const provider = this.settings.get('debug.link-resolver.source');
|
let provider = this.settings.get('debug.link-resolver.source').value;
|
||||||
let examples;
|
if ( provider === 'special:socket')
|
||||||
if ( provider === 'dev' ) {
|
provider = LINK_DATA_HOSTS.test.value;
|
||||||
try {
|
|
||||||
examples = (await timeout(fetch('https://localhost:8002/examples'), 15000).then(resp => resp.ok ? resp.json() : null)).examples;
|
|
||||||
} catch(err) {
|
|
||||||
console.error(err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( ! examples ) {
|
let examples;
|
||||||
try {
|
try {
|
||||||
examples = (await timeout(fetch('https://api-test.frankerfacez.com/v2/link/examples'), 15000).then(resp => resp.ok ? resp.json() : null)).examples;
|
examples = (await timeout(fetch(`${provider}/examples`), 15000).then(resp => resp.ok ? resp.json() : null)).examples;
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if ( ! examples )
|
if ( ! examples )
|
||||||
examples = [];
|
examples = [];
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
<template lang="html">
|
<template lang="html">
|
||||||
<div
|
<div
|
||||||
|
:style="{zIndex: z}"
|
||||||
:class="{ maximized: maximized || exclusive, exclusive, faded }"
|
:class="{ maximized: maximized || exclusive, exclusive, faded }"
|
||||||
class="ffz-dialog ffz-main-menu tw-elevation-3 tw-c-background-alt tw-c-text-base tw-border tw-flex tw-flex-nowrap tw-flex-column"
|
class="ffz-dialog ffz-main-menu tw-elevation-3 tw-c-background-alt tw-c-text-base tw-border tw-flex tw-flex-nowrap tw-flex-column"
|
||||||
|
@focusin="onFocus"
|
||||||
|
@click="onFocus"
|
||||||
>
|
>
|
||||||
<header ref="header" class="tw-c-background-base tw-full-width tw-align-items-center tw-flex tw-flex-nowrap" @dblclick="maybeResize($event)">
|
<header ref="header" class="tw-c-background-base tw-full-width tw-align-items-center tw-flex tw-flex-nowrap" @dblclick="maybeResize($event)">
|
||||||
<h3 class="ffz-i-zreknarf ffz-i-pd-1">
|
<h3 class="ffz-i-zreknarf ffz-i-pd-1">
|
||||||
|
@ -123,10 +126,15 @@
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
import displace from 'displacejs';
|
import displace from 'displacejs';
|
||||||
|
import { getDialogNextZ } from 'src/utilities/dialog';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
return this.$vnode.data;
|
const out = this.$vnode.data;
|
||||||
|
|
||||||
|
out.z = getDialogNextZ();
|
||||||
|
|
||||||
|
return out;
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -242,6 +250,8 @@ export default {
|
||||||
handle: this.$el.querySelector('header'),
|
handle: this.$el.querySelector('header'),
|
||||||
highlightInputs: true,
|
highlightInputs: true,
|
||||||
constrain: true,
|
constrain: true,
|
||||||
|
onMouseDown: () => this.onFocus(),
|
||||||
|
onTouchStart: () => this.onFocus(),
|
||||||
ignoreFn(event) {
|
ignoreFn(event) {
|
||||||
return event.target.closest('button') != null
|
return event.target.closest('button') != null
|
||||||
}
|
}
|
||||||
|
@ -249,6 +259,11 @@ export default {
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onFocus() {
|
||||||
|
if ( ! this.exclusive )
|
||||||
|
this.z = getDialogNextZ();
|
||||||
|
},
|
||||||
|
|
||||||
handleResize() {
|
handleResize() {
|
||||||
if ( this.displace )
|
if ( this.displace )
|
||||||
this.displace.reinit();
|
this.displace.reinit();
|
||||||
|
|
|
@ -144,7 +144,7 @@ export default class PubSubClient extends Module {
|
||||||
try {
|
try {
|
||||||
const mqtt = await this.loadMQTT();
|
const mqtt = await this.loadMQTT();
|
||||||
client = this._client = mqtt.mqtt_v5({
|
client = this._client = mqtt.mqtt_v5({
|
||||||
|
keep_alive: 30
|
||||||
})
|
})
|
||||||
.with_websock(cluster)
|
.with_websock(cluster)
|
||||||
.with_autoreconnect();
|
.with_autoreconnect();
|
||||||
|
|
|
@ -439,4 +439,4 @@ Twilight.ROUTES = {
|
||||||
|
|
||||||
Twilight.DIALOG_EXCLUSIVE = '.moderation-root,.sunlight-root,.twilight-main,.twilight-minimal-root>div,#root>div>.tw-full-height,.clips-root,#root';
|
Twilight.DIALOG_EXCLUSIVE = '.moderation-root,.sunlight-root,.twilight-main,.twilight-minimal-root>div,#root>div>.tw-full-height,.clips-root,#root';
|
||||||
Twilight.DIALOG_MAXIMIZED = '.moderation-view-page > div[data-highlight-selector="main-grid"],.sunlight-page,.twilight-main,.twilight-minimal-root,#root .dashboard-side-nav+.tw-full-height,.clips-root>.tw-full-height .scrollable-area,.teams-page-body__outer-container .scrollable-area';
|
Twilight.DIALOG_MAXIMIZED = '.moderation-view-page > div[data-highlight-selector="main-grid"],.sunlight-page,.twilight-main,.twilight-minimal-root,#root .dashboard-side-nav+.tw-full-height,.clips-root>.tw-full-height .scrollable-area,.teams-page-body__outer-container .scrollable-area';
|
||||||
Twilight.DIALOG_SELECTOR = '.moderation-root,.sunlight-root,#root>div,.twilight-minimal-root>.tw-full-height,.clips-root>.tw-full-height .scrollable-area';
|
Twilight.DIALOG_SELECTOR = '.moderation-root,.sunlight-root,#root,.twilight-minimal-root>.tw-full-height,.clips-root>.tw-full-height .scrollable-area';
|
|
@ -207,7 +207,7 @@ export default class Channel extends Module {
|
||||||
if ( ! inst._ffz_tips ) {
|
if ( ! inst._ffz_tips ) {
|
||||||
const tt = this.resolve('tooltips');
|
const tt = this.resolve('tooltips');
|
||||||
if ( tt ) {
|
if ( tt ) {
|
||||||
inst._ffz_tips = tt._createInstance(el, 'ffz-link', 'link', tt.getRoot());
|
inst._ffz_tips = tt._createInstance(el, 'tw-link', 'link', tt.getRoot());
|
||||||
inst._ffz_tip_el = el;
|
inst._ffz_tip_el = el;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -240,10 +240,39 @@ export const WS_CLUSTERS = {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export const LINK_DATA_HOSTS = {
|
||||||
|
socket: {
|
||||||
|
title: 'Socket Cluster (Deprecated)',
|
||||||
|
value: 'special:socket'
|
||||||
|
},
|
||||||
|
localhost: {
|
||||||
|
title: 'Local Dev Server (Port 8002)',
|
||||||
|
value: 'https://localhost:8002',
|
||||||
|
has_sse: true
|
||||||
|
},
|
||||||
|
'localhost-cf': {
|
||||||
|
title: 'Local Dev Worker (Wrangler, Port 8787)',
|
||||||
|
value: 'https://localhost:8787'
|
||||||
|
},
|
||||||
|
test: {
|
||||||
|
title: 'API Test Server',
|
||||||
|
value: 'https://api-test.frankerfacez.com/v2/link'
|
||||||
|
},
|
||||||
|
'test-cf': {
|
||||||
|
title: 'Cloudflare Test Worker',
|
||||||
|
value: 'https://link-service.workers.frankerfacez.com'
|
||||||
|
},
|
||||||
|
Production: {
|
||||||
|
title: 'Production',
|
||||||
|
value: 'https://api.frankerfacez.com/v2/link'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
export const PUBSUB_CLUSTERS = {
|
export const PUBSUB_CLUSTERS = {
|
||||||
Production: 'wss://pubsub.frankerfacez.com/mqtt',
|
Production: 'wss://pubsub.frankerfacez.com/mqtt',
|
||||||
Staging: 'wss://pubsub-staging.frankerfacez.com/mqtt',
|
Staging: 'wss://pubsub-staging.frankerfacez.com/mqtt',
|
||||||
Development: 'wss://127.0.0.1:8084/mqtt'
|
Development: 'wss://stendec.dev/mqtt/ws'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,14 @@ import {EventEmitter} from 'utilities/events';
|
||||||
|
|
||||||
import Site from 'site';
|
import Site from 'site';
|
||||||
|
|
||||||
|
|
||||||
|
let last_z = 9000;
|
||||||
|
|
||||||
|
export function getDialogNextZ() {
|
||||||
|
return last_z++;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
export class Dialog extends EventEmitter {
|
export class Dialog extends EventEmitter {
|
||||||
constructor(element, options = {}) {
|
constructor(element, options = {}) {
|
||||||
super();
|
super();
|
||||||
|
@ -205,9 +213,6 @@ export class Dialog extends EventEmitter {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Dialog.lastZ = 99999999;
|
|
||||||
|
|
||||||
|
|
||||||
Dialog.EXCLUSIVE = Site.DIALOG_EXCLUSIVE;
|
Dialog.EXCLUSIVE = Site.DIALOG_EXCLUSIVE;
|
||||||
Dialog.MAXIMIZED = Site.DIALOG_MAXIMIZED;
|
Dialog.MAXIMIZED = Site.DIALOG_MAXIMIZED;
|
||||||
Dialog.SELECTOR = Site.DIALOG_SELECTOR;
|
Dialog.SELECTOR = Site.DIALOG_SELECTOR;
|
||||||
|
|
|
@ -8,7 +8,7 @@ import {has} from 'utilities/object';
|
||||||
import Markdown from 'markdown-it';
|
import Markdown from 'markdown-it';
|
||||||
import MILA from 'markdown-it-link-attributes';
|
import MILA from 'markdown-it-link-attributes';
|
||||||
|
|
||||||
export const VERSION = 8;
|
export const VERSION = 9;
|
||||||
|
|
||||||
export const TOKEN_TYPES = {};
|
export const TOKEN_TYPES = {};
|
||||||
|
|
||||||
|
@ -966,6 +966,53 @@ TOKEN_TYPES.i18n = function(token, createElement, ctx) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Token Type: I18n Select
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
function findMatchingLocale(locale, list) {
|
||||||
|
|
||||||
|
// Is the locale present exactly?
|
||||||
|
for(const item of list) {
|
||||||
|
if ( item.localeCompare(locale, undefined, {sensitivity: 'accent'}) === 0 )
|
||||||
|
return locale;
|
||||||
|
}
|
||||||
|
|
||||||
|
// What about partials?
|
||||||
|
let prefixed = `${locale.toLowerCase()}-`;
|
||||||
|
for(const item of list) {
|
||||||
|
if ( item.toLowerCase().startsWith(prefixed) )
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Last resort, do we have a - in the locale?
|
||||||
|
const idx = locale.indexOf('-');
|
||||||
|
if ( idx !== -1 )
|
||||||
|
return findMatchingLocale(locale.slice(0, idx), list);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
TOKEN_TYPES.i18n_select = function(token, createElement, ctx) {
|
||||||
|
|
||||||
|
// What locale and choices do we have.
|
||||||
|
const choices = token.choices || {};
|
||||||
|
let locale = ctx.i18n?.locale ?? 'en';
|
||||||
|
|
||||||
|
// Try to find a valid match, or use the default.
|
||||||
|
let selected = findMatchingLocale(locale, Object.keys(choices));
|
||||||
|
if ( ! selected )
|
||||||
|
selected = token.default;
|
||||||
|
|
||||||
|
// Render it.
|
||||||
|
return renderTokens(
|
||||||
|
choices[selected],
|
||||||
|
createElement,
|
||||||
|
ctx,
|
||||||
|
token.markdown
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// Token Type: Link
|
// Token Type: Link
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
@ -1078,19 +1125,25 @@ TOKEN_TYPES.overlay = function(token, createElement, ctx) {
|
||||||
|
|
||||||
function handlePlayerClick(token, id, ctx, event) {
|
function handlePlayerClick(token, id, ctx, event) {
|
||||||
//console.log('clicked player', token, id, ctx, event);
|
//console.log('clicked player', token, id, ctx, event);
|
||||||
if ( ctx.togglePlayer ) {
|
const target = event.currentTarget,
|
||||||
ctx.togglePlayer(id);
|
is_av = target instanceof HTMLVideoElement || target instanceof HTMLAudioElement;
|
||||||
|
|
||||||
|
if ( is_av || ctx.togglePlayer ) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
}
|
}
|
||||||
|
|
||||||
const target = event.currentTarget;
|
if ( is_av ) {
|
||||||
if ( target instanceof HTMLVideoElement ) {
|
|
||||||
if ( target.paused )
|
if ( target.paused )
|
||||||
target.play();
|
target.play();
|
||||||
else
|
else
|
||||||
target.pause();
|
target.pause();
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( ctx.togglePlayer )
|
||||||
|
ctx.togglePlayer(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
TOKEN_TYPES.player = function(token, createElement, ctx) {
|
TOKEN_TYPES.player = function(token, createElement, ctx) {
|
||||||
|
@ -1101,6 +1154,9 @@ TOKEN_TYPES.player = function(token, createElement, ctx) {
|
||||||
|
|
||||||
const handler = handlePlayerClick.bind(this, token, id, ctx);
|
const handler = handlePlayerClick.bind(this, token, id, ctx);
|
||||||
|
|
||||||
|
if ( ! active && (token.content || token.iframe) )
|
||||||
|
return render_player_content(id, handler, token, createElement, ctx);
|
||||||
|
|
||||||
if ( token.iframe )
|
if ( token.iframe )
|
||||||
return render_player_iframe(id, active ?? false, handler, token, createElement, ctx);
|
return render_player_iframe(id, active ?? false, handler, token, createElement, ctx);
|
||||||
|
|
||||||
|
@ -1120,7 +1176,7 @@ TOKEN_TYPES.player = function(token, createElement, ctx) {
|
||||||
style.aspectRatio = aspect;
|
style.aspectRatio = aspect;
|
||||||
|
|
||||||
if ( ctx.vue )
|
if ( ctx.vue )
|
||||||
return createElement('video', {
|
return createElement(token.audio ? 'audio' : 'video', {
|
||||||
style,
|
style,
|
||||||
attrs: {
|
attrs: {
|
||||||
autoplay: playing,
|
autoplay: playing,
|
||||||
|
@ -1141,7 +1197,7 @@ TOKEN_TYPES.player = function(token, createElement, ctx) {
|
||||||
}
|
}
|
||||||
})));
|
})));
|
||||||
|
|
||||||
return createElement('video', {
|
return createElement(token.audio ? 'audio' : 'video', {
|
||||||
style,
|
style,
|
||||||
muted,
|
muted,
|
||||||
autoplay: playing,
|
autoplay: playing,
|
||||||
|
@ -1155,9 +1211,32 @@ TOKEN_TYPES.player = function(token, createElement, ctx) {
|
||||||
})));
|
})));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function render_player_content(id, handler, token, createElement, ctx) {
|
||||||
|
const content = renderTokens(token.content, createElement, ctx, token.markdown),
|
||||||
|
classes = ['ffz--rich-player'],
|
||||||
|
style = {};
|
||||||
|
|
||||||
|
if ( token.aspect )
|
||||||
|
style.aspectRatio = token.aspect;
|
||||||
|
|
||||||
|
if ( ctx.vue )
|
||||||
|
return createElement('div', {
|
||||||
|
class: classes,
|
||||||
|
style,
|
||||||
|
on: {
|
||||||
|
click: handler
|
||||||
|
}
|
||||||
|
}, content);
|
||||||
|
|
||||||
|
return createElement('div', {
|
||||||
|
className: classes.join(' '),
|
||||||
|
onClick: handler,
|
||||||
|
style
|
||||||
|
}, content);
|
||||||
|
}
|
||||||
|
|
||||||
function render_player_iframe(id, active, handler, token, createElement, ctx) {
|
function render_player_iframe(id, active, handler, token, createElement, ctx) {
|
||||||
|
|
||||||
if ( active ) {
|
|
||||||
const style = {},
|
const style = {},
|
||||||
attrs = {
|
attrs = {
|
||||||
src: token.iframe,
|
src: token.iframe,
|
||||||
|
@ -1180,29 +1259,7 @@ function render_player_iframe(id, active, handler, token, createElement, ctx) {
|
||||||
style,
|
style,
|
||||||
...attrs
|
...attrs
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
const content = renderTokens(token.content, createElement, ctx, token.markdown),
|
|
||||||
classes = ['ffz--rich-player'],
|
|
||||||
style = {};
|
|
||||||
|
|
||||||
if ( token.aspect )
|
|
||||||
style.aspectRatio = token.aspect;
|
|
||||||
|
|
||||||
if ( ctx.vue )
|
|
||||||
return createElement('div', {
|
|
||||||
class: classes,
|
|
||||||
style,
|
|
||||||
on: {
|
|
||||||
click: handler
|
|
||||||
}
|
|
||||||
}, content);
|
|
||||||
|
|
||||||
return createElement('div', {
|
|
||||||
className: classes.join(' '),
|
|
||||||
onClick: handler,
|
|
||||||
style
|
|
||||||
}, content);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -121,6 +121,10 @@ export const DEFAULT_FORMATS = {
|
||||||
},
|
},
|
||||||
percent: {
|
percent: {
|
||||||
style: 'percent'
|
style: 'percent'
|
||||||
|
},
|
||||||
|
long_percent: {
|
||||||
|
style: 'percent',
|
||||||
|
minimumFractionDigits: 2
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -386,10 +386,18 @@
|
||||||
|
|
||||||
&[data-items="2"] > * {
|
&[data-items="2"] > * {
|
||||||
min-height: calc(50% - 2px);
|
min-height: calc(50% - 2px);
|
||||||
|
|
||||||
|
img, video {
|
||||||
|
max-height: calc(calc(350px + 4px) / 2);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&[data-items="1"] > * {
|
&[data-items="1"] > * {
|
||||||
min-height: 100%;
|
min-height: 100%;
|
||||||
|
|
||||||
|
img, video {
|
||||||
|
max-height: calc(350px + 4px);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
img, video {
|
img, video {
|
||||||
|
|
|
@ -80,7 +80,7 @@
|
||||||
.ffz-has-dialog {
|
.ffz-has-dialog {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
& > :not(.ffz-dialog):not(.ffz__tooltip) {
|
& > :not(.ffz-dialog):not(.ffz__tooltip):not(.ffz-viewer-card) {
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Add table
Add a link
Reference in a new issue