1
0
Fork 0
mirror of https://github.com/FrankerFaceZ/FrankerFaceZ.git synced 2025-08-03 08:28:31 +00:00

Render badges. Fix badges with weird font sizes. Add an option to show metadata in theatre mode. Fixes #364. Fixes #363.

This commit is contained in:
SirStendec 2018-01-16 17:36:56 -05:00
parent 95d0002b56
commit 0754144c36
8 changed files with 170 additions and 52 deletions

View file

@ -1,3 +1,10 @@
<div class="list-header">4.0.0-beta1.5<span>@5aed9c5b086948ebcfc3</span> <time datetime="2018-01-16">(2018-01-16)</time></div>
<ul class="chat-menu-content menu-side-padding">
<li>Added: Render badges added by FrankerFaceZ and other extensions using FFZ's API.</li>
<li>Added: Option to show the channel metadata bar when hovering over the player in theatre mode.</li>
<li>Fixed: Badges and chat font size displaying incorrectly when the browser's font size is not set to default.</li>
</ul>
<div class="list-header">4.0.0-beta1.5<span>@68369664d8835665997c</span> <time datetime="2018-01-15">(2018-01-15)</time></div>
<ul class="chat-menu-content menu-side-padding">
<li>Fixed: Chat Freeze breaking due to Twitch moving the <code>scrollToBottom</code> function out of the prototype and defining it on the chat pane during construction.</li>

View file

@ -25,20 +25,45 @@ export const CSS_BADGES = {
subscriber: { 0: { color: '#6441a4' }, 1: { color: '#6441a4' }},
}
export const BADGE_POSITIONS = {
broadcaster: 0,
staff: 0,
admin: 0,
global_mod: 0,
mod: 1,
moderator: 1,
twitchbot: 1,
subscriber: 25
};
const NO_REPEAT = 'background-repeat:no-repeat;background-position:center;',
BASE_IMAGE = 'https://cdn.frankerfacez.com/badges/twitch/',
CSS_TEMPLATES = {
0: data => `background:${data.image} ${data.color};${data.svg ? `background-size:${data.scale*18}px;` : `background-image:${data.image_set};`}${NO_REPEAT}`,
1: data => `${CSS_TEMPLATES[0](data)}border-radius:${data.scale*2}px;`,
2: data => `${CSS_TEMPLATES[0](data)}border-radius:${data.scale*9}px;background-size:${data.scale*16}px;`,
3: data => `background:${data.color};border-radius:${data.scale*9}px;`,
4: data => `${CSS_TEMPLATES[3](data)}height:${data.scale*10}px;min-width:${data.scale*10}px;`,
5: data => `background:${data.image};${data.svg ? `background-size:${data.scale*18}px;` : `background-image:${data.image_set};`}${NO_REPEAT}`,
6: data => `background:linear-gradient(${data.color},${data.color});${WEBKIT}mask-image:${data.image};${data.svg ? `${WEBKIT}mask-size:${data.scale*18}px ${data.scale*18}px;` : `${WEBKIT}mask-image:${data.image_set};`}`
0: data => `background:${data.image} ${data.color};background-size:${data.scale*1.8}rem;${data.svg ? '' : `background-image:${data.image_set};`}${NO_REPEAT}`,
1: data => `${CSS_TEMPLATES[0](data)}border-radius:${data.scale*.2}rem;`,
2: data => `${CSS_TEMPLATES[0](data)}border-radius:${data.scale*.9}rem;background-size:${data.scale*1.6}rem;`,
3: data => `background:${data.color};border-radius:${data.scale*.9}rem;`,
4: data => `${CSS_TEMPLATES[3](data)}height:${data.scale}rem;min-width:${data.scale}rem;`,
5: data => `background:${data.image};background-size:${data.scale*1.8}rem;${data.svg ? `` : `background-image:${data.image_set};`}${NO_REPEAT}`,
6: data => `background:linear-gradient(${data.color},${data.color});${WEBKIT}mask-image:${data.image};${WEBKIT}mask-size:${data.scale*1.8}rem ${data.scale*1.8}rem;${data.svg ? `` : `${WEBKIT}mask-image:${data.image_set};`}`
};
export function generateOverrideCSS(data, style, is_dark) {
let image = `url("${data.urls[1]}")`,
image_set = `${WEBKIT}image-set(${image} 1x${data.urls[2] ? `, url("${data.urls[2]}") 2x` : ''}${data.urls[4] ? `, url("${data.urls[4]}") 4x` : ''})`;
if ( style === 3 || style === 4 )
return '';
if ( style === 6 )
return `${WEBKIT}mask-image:${image} !important;${WEBKIT}mask-image:${image_set} !important;`;
else
return `background-image:${image} !important;background-image:${image_set} !important;`;
}
export function generateBadgeCSS(badge, version, data, style, is_dark, scale = 1) {
let color = data.color || 'transparent',
base_image = data.image || `${BASE_IMAGE}${badge}${data.svg ? '.svg' : `/${version}/`}`,
@ -72,9 +97,15 @@ export function generateBadgeCSS(badge, version, data, style, is_dark, scale = 1
style = 0;
svg = base_image.endsWith('.svg');
image = `url("${svg ? base_image : `${base_image}${scale}${trans ? '_trans' : ''}.png`}")`;
if ( data.urls )
image = `url("${data.urls[1]}")`;
else
image = `url("${svg ? base_image : `${base_image}${scale}${trans ? '_trans' : ''}.png`}")`;
if ( svg && scale < 4 ) {
if ( data.urls ) {
image_set = `${WEBKIT}image-set(${image} 1x${data.urls[2] ? `, url("${data.urls[2]}") 2x` : ''}${data.urls[4] ? `url("${data.urls[4]}") 4x` : ''})`
} else if ( ! svg && scale < 4 ) {
if ( scale === 1 )
image_set = `${WEBKIT}image-set(${image} 1x, url("${base_image}2${trans ? '_trans' : ''}.png") 2x, url("${base_image}4${trans ? '_trans' : ''}.png") 4x)`;
@ -85,7 +116,8 @@ export function generateBadgeCSS(badge, version, data, style, is_dark, scale = 1
image_set = svg;
}
return `${invert ? 'filter:invert(100%);' : ''}${CSS_TEMPLATES[style]({
// TODO: Fix the click_url name once we actually support badge clicking.
return `${data.__click_url ? 'cursor:pointer;' : ''}${invert ? 'filter:invert(100%);' : ''}${CSS_TEMPLATES[style]({
scale,
color,
image,
@ -137,13 +169,19 @@ export default class Badges extends Module {
this.rebuildAllCSS();
this.loadGlobalBadges();
// TODO: Better tooltips. Especially support for custom colors, titles, etc. from
// user-specific badges.
this.tooltips.types.badge = (target, tip) => {
const container = target.parentElement.parentElement,
provider = target.dataset.provider,
replaced = target.dataset.replaced,
badge = target.dataset.badge,
version = target.dataset.version,
room_id = container.dataset.roomId,
room_login = container.dataset.room;
room_login = container.dataset.room,
replaced_data = replaced && this.badges[replaced];
let preview, title;
@ -163,8 +201,8 @@ export default class Badges extends Module {
preview = e('span', {
className: 'preview-image ffz-badge',
style: {
height: '72px',
width: '72px',
height: '7.2rem',
width: '7.2rem',
backgroundColor: data.color,
backgroundImage: `url("${data.urls[4]}")`
}
@ -195,33 +233,65 @@ export default class Badges extends Module {
render(msg, e) { // eslint-disable-line class-methods-use-this
const out = [],
twitch_badges = msg.badges || {};
const hidden_badges = this.parent.context.get('chat.badges.hidden') || [],
out = [],
slotted = {},
twitch_badges = msg.badges || {},
/*user = msg.user || {},
user = msg.user || {},
user_id = user.userID,
user_login = user.userLogin,
room_id = msg.roomID,
room_login = msg.roomLogin,
badges = this.getBadges(user_id, user_login, room_id, room_login);*/
badges = this.getBadges(user_id, user_login, room_id, room_login);
let last_slot = 50, slot;
for(const badge_id in twitch_badges)
if ( has(twitch_badges, badge_id) ) {
const version = twitch_badges[badge_id];
out.push(e('span', {
className: 'ffz-tooltip ffz-badge',
'data-tooltip-type': 'badge',
'data-provider': 'twitch',
'data-badge': badge_id,
'data-version': version
}));
const version = twitch_badges[badge_id],
is_game = badge_id.endsWith('_1');
if ( hidden_badges.includes(badge_id) || (is_game && hidden_badges.includes('game')) )
continue;
if ( has(BADGE_POSITIONS, badge_id) )
slot = BADGE_POSITIONS[badge_id];
else
slot = last_slot++;
slotted[slot] = {
id: badge_id,
props: {
'data-provider': 'twitch',
'data-badge': badge_id,
'data-version': version
}
};
}
/*for(const badge of badges)
for(const badge of badges)
if ( badge && badge.id ) {
if ( hidden_badges.includes(badge.id) )
continue;
const full_badge = this.badges[badge.id],
style = {},
slot = has(badge, 'slot') ? badge.slot : full_badge.slot,
old_badge = slotted[slot];
if ( old_badge ) {
const replaces = has(badge, 'replaces') ? badge.replaces : full_badge.replaces,
replaces_type = badge.replaces_type || full_badge.replaces_type;
if ( replaces && (!replaces_type || replaces_type === old_badge.id) )
old_badge.replaced = badge.id;
continue;
} else if ( ! slot )
continue;
const style = {},
props = {
className: 'ffz-tooltip ffz-badge',
'data-tooltip-type': 'badge',
@ -230,14 +300,22 @@ export default class Badges extends Module {
style
};
if ( full_badge.image )
style.backgroundImage = `url("${full_badge.image}")`;
slotted[slot] = { id: badge.id, props };
}
if ( full_badge.color )
style.backgroundColor = full_badge.color;
for(const slot in slotted)
if ( has(slotted, slot) ) {
const data = slotted[slot],
props = data.props;
props.className = 'ffz-tooltip ffz-badge';
props['data-tooltip-type'] = 'badge';
if ( data.replaced )
props['data-replaced'] = data.replaced;
out.push(e('span', props));
}*/
}
return out;
}
@ -247,6 +325,7 @@ export default class Badges extends Module {
for(const room of this.parent.iterateRooms())
room.buildBadgeCSS();
this.buildBadgeCSS();
this.buildTwitchBadgeCSS();
this.buildTwitchCSSBadgeCSS();
}
@ -316,6 +395,7 @@ export default class Badges extends Module {
}
this.log.info(`Loaded ${badges} badges and assigned them to ${users} users.`);
this.buildBadgeCSS();
}
@ -332,6 +412,24 @@ export default class Badges extends Module {
}
buildBadgeCSS() {
const style = this.parent.context.get('chat.badges.style'),
is_dark = this.parent.context.get('theme.is-dark');
const out = [];
for(const key in this.badges)
if ( has(this.badges, key) ) {
const data = this.badges[key],
selector = `.ffz-badge[data-badge="${key}"]`;
out.push(`${selector}{${generateBadgeCSS(key, 0, data, style, is_dark)}}`);
out.push(`.ffz-badge[data-replaced="${key}"]{${generateOverrideCSS(data, style, is_dark)}}`);
}
this.style.set('ext-badges', out.join('\n'));
}
// ========================================================================
// Twitch Badges
// ========================================================================
@ -394,6 +492,7 @@ export default class Badges extends Module {
background-color: transparent;
filter: none;
${WEBKIT}mask-image: none;
background-size: 1.8rem;
background-image: url("${data.image1x}");
background-image: ${WEBKIT}image-set(
url("${data.image1x}") 1x,

View file

@ -286,6 +286,7 @@ export default class Room {
background-color: transparent;
filter: none;
${WEBKIT}mask-image: none;
background-size: 1.8rem;
background-image: url("${data.image1x}");
background-image: ${WEBKIT}image-set(
url("${data.image1x}") 1x,

View file

@ -199,19 +199,6 @@ export default class Metadata extends Module {
}
async getData(key) {
const def = this.definitions[key];
if ( ! def )
return {label: null};
return {
icon: maybe_call(def.icon),
label: maybe_call(def.label),
refresh: maybe_call(def.refresh)
}
}
updateMetadata(keys) {
const bar = this.resolve('site.channel_bar');
if ( bar ) {
@ -329,7 +316,7 @@ export default class Metadata extends Module {
});
if ( def.popup )
popup.addEventListener('click', e => {
popup.addEventListener('click', () => {
if ( popup.disabled || popup.classList.contains('disabled') || el.disabled || el.classList.contains('disabled') )
return false;

View file

@ -130,7 +130,7 @@ export default class ChatHook extends Module {
ui: {
path: 'Chat > Appearance >> General @{"sort": -1}',
title: 'Width',
description: 'How wide chat should be, in pixels.',
description: "How wide chat should be, in pixels. This may be affected by your browser's zoom and font size settings.",
component: 'setting-text-box',
process(val) {
val = parseInt(val, 10);
@ -147,7 +147,7 @@ export default class ChatHook extends Module {
ui: {
path: 'Chat > Appearance >> General',
title: 'Font Size',
description: 'How large should text in chat be, in pixels.',
description: "How large should text in chat be, in pixels. This may be affected by your browser's zoom and font size settings.",
component: 'setting-text-box',
process(val) {
val = parseInt(val, 10);
@ -247,10 +247,10 @@ export default class ChatHook extends Module {
if ( font.indexOf(' ') !== -1 && font.indexOf(',') === -1 && font.indexOf('"') === -1 && font.indexOf("'") === -1 )
font = `"${font}"`;
this.css_tweaks.setVariable('chat-font-size', `${size}px`);
this.css_tweaks.setVariable('chat-line-height', `${lh}px`);
this.css_tweaks.setVariable('chat-font-size', `${size/10}rem`);
this.css_tweaks.setVariable('chat-line-height', `${lh/10}rem`);
this.css_tweaks.setVariable('chat-font-family', font);
this.css_tweaks.setVariable('chat-width', `${width}px`);
this.css_tweaks.setVariable('chat-width', `${width/10}rem`);
this.css_tweaks.toggle('chat-font', size !== 12 || font);
this.css_tweaks.toggle('chat-width', width !== 340);

View file

@ -0,0 +1,12 @@
.channel-page-layout__scroll-area--theatre-mode .channel-info-bar {
position: fixed;
bottom: 100px;
left: 50px;
right: calc(var(--ffz-chat-width) + 250px);
z-index: 3500;
opacity: 0;
}
.channel-page-layout__scroll-area--theatre-mode:hover .channel-info-bar {
opacity: 0.75;
}

View file

@ -60,6 +60,17 @@ export default class Player extends Module {
changed: val => this.css_tweaks.toggle('theatre-no-whispers', val)
});
this.settings.add('player.theatre.metadata', {
default: false,
ui: {
path: 'Channel > Player >> Theatre Mode',
title: 'Show metadata when mousing over the player.',
component: 'setting-check-box'
},
changed: val => this.css_tweaks.toggle('theatre-metadata', val)
});
this.settings.add('player.theatre.auto-enter', {
default: false,
ui: {
@ -127,6 +138,7 @@ export default class Player extends Module {
this.css_tweaks.toggle('player-volume', this.settings.get('player.volume-always-shown'));
this.css_tweaks.toggle('player-ext-mouse', !this.settings.get('player.ext-interaction'));
this.css_tweaks.toggle('theatre-no-whispers', this.settings.get('player.theatre.no-whispers'));
this.css_tweaks.toggle('theatre-metadata', this.settings.get('player.theatre.metadata'));
this.updateHideExtensions();
const t = this;

View file

@ -3,7 +3,7 @@ body {
}
.ffz-metadata-balloon {
z-index: 999999999;
z-index: 999999999 !important;
margin: 6px;
&[x-placement^="bottom"] > .tw-balloon__tail {