diff --git a/package.json b/package.json index 53b3424e..6734d3aa 100755 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "frankerfacez", "author": "Dan Salvato LLC", - "version": "4.17.2", + "version": "4.17.3", "description": "FrankerFaceZ is a Twitch enhancement suite.", "license": "Apache-2.0", "scripts": { diff --git a/src/modules/chat/badges.jsx b/src/modules/chat/badges.jsx index b839f4e5..32dee1b4 100644 --- a/src/modules/chat/badges.jsx +++ b/src/modules/chat/badges.jsx @@ -4,7 +4,7 @@ // Badge Handling // ============================================================================ -import {NEW_API, SERVER, API_SERVER, IS_WEBKIT, WEBKIT_CSS as WEBKIT} from 'utilities/constants'; +import {NEW_API, SERVER, API_SERVER, IS_WEBKIT, IS_FIREFOX, WEBKIT_CSS as WEBKIT} from 'utilities/constants'; import {createElement, ManagedStyle} from 'utilities/dom'; import {has} from 'utilities/object'; @@ -126,11 +126,11 @@ export function generateBadgeCSS(badge, version, data, style, is_dark, badge_ver if ( style !== 3 && style !== 4 ) { svg = base_image.endsWith('.svg'); if ( data.urls ) - image = `url("${data.urls[1]}")`; + image = `url("${data.urls[scale]}")`; else image = `url("${svg ? base_image : `${base_image}${scale}${trans ? '_trans' : ''}.png`}")`; - if ( data.urls ) { + if ( data.urls && scale === 1 ) { 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 ) { @@ -141,7 +141,7 @@ export function generateBadgeCSS(badge, version, data, style, is_dark, badge_ver image_set = `${WEBKIT}image-set(${image} 1x, url("${base_image}4${trans ? '_trans' : ''}.png") 2x)`; } else - image_set = svg; + image_set = image; } if ( color_fixer && color && color !== 'transparent' ) @@ -149,7 +149,7 @@ export function generateBadgeCSS(badge, version, data, style, is_dark, badge_ver // 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, + scale: 1, color, image, image_set, @@ -172,6 +172,17 @@ export default class Badges extends Module { this.badges = {}; this.twitch_badges = {}; + if ( IS_FIREFOX ) + this.settings.add('chat.badges.media-queries', { + default: true, + ui: { + path: 'Chat > Badges >> tabs ~> Appearance', + title: 'Use @media queries to support High-DPI Badge images in Mozilla Firefox.', + description: 'This is required to see high-DPI badges on Firefox because Firefox still has yet to support `image-set()` after more than five years. It may be less reliable.', + component: 'setting-check-box' + } + }); + this.settings.add('chat.badges.version', { default: 2, ui: { @@ -304,6 +315,7 @@ export default class Badges extends Module { this.parent.context.on('changed:theme.is-dark', this.rebuildAllCSS, this); this.parent.context.on('changed:theme.tooltips-dark', this.rebuildAllCSS, this); this.parent.context.on('changed:chat.badges.version', this.rebuildAllCSS, this); + this.parent.context.on('changed:chat.badges.media-queries', this.rebuildAllCSS, this); this.parent.context.on('changed:chat.badges.fix-colors', this.rebuildColoredBadges, this); this.rebuildAllCSS(); @@ -684,7 +696,8 @@ 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'); + is_dark = this.parent.context.get('theme.is-dark'), + use_media = IS_FIREFOX && this.parent.context.get('chat.badges.media-queries'); const out = []; for(const key in this.badges) @@ -692,8 +705,14 @@ export default class Badges extends Module { const data = this.badges[key], selector = `.ffz-badge[data-badge="${key}"]`; - out.push(`${selector}{${generateBadgeCSS(key, 0, data, style, is_dark, 0, this.color_fixer)}}`); out.push(`.ffz-badge[data-replaced="${key}"]{${generateOverrideCSS(data, style, is_dark)}}`); + + if ( use_media ) { + out.push(`@media (max-resolution: 99dpi) {${selector}{${generateBadgeCSS(key, 0, data, style, is_dark, 0, this.color_fixer, 1)}}}`); + out.push(`@media (min-resolution: 100dpi) and (max-resolution:199dpi) {${selector}{${generateBadgeCSS(key, 0, data, style, is_dark, 0, this.color_fixer, 2)}}}`); + out.push(`@media (min-resolution: 200dpi) {${selector}{${generateBadgeCSS(key, 0, data, style, is_dark, 0, this.color_fixer, 4)}}}`); + } else + out.push(`${selector}{${generateBadgeCSS(key, 0, data, style, is_dark, 0, this.color_fixer)}}`); } this.style.set('ext-badges', out.join('\n')); @@ -758,6 +777,7 @@ export default class Badges extends Module { buildTwitchCSSBadgeCSS() { const style = this.parent.context.get('chat.badges.style'), is_dark = this.parent.context.get('theme.is-dark'), + use_media = IS_FIREFOX && this.parent.context.get('chat.badges.media-queries'), badge_version = this.parent.context.get('chat.badges.version'), versioned = CSS_BADGES[badge_version] || {}; @@ -771,7 +791,12 @@ export default class Badges extends Module { const d = data[version], selector = `.ffz-badge[data-badge="${key}"][data-version="${version}"]`; - out.push(`${selector}{${generateBadgeCSS(key, version, d, style, is_dark, badge_version, this.color_fixer)}}`); + if ( use_media ) { + out.push(`@media (max-resolution: 99dpi) {${selector}{${generateBadgeCSS(key, version, d, style, is_dark, badge_version, this.color_fixer, 1)}}}`); + out.push(`@media (min-resolution: 100dpi) and (max-resolution:199dpi) {${selector}{${generateBadgeCSS(key, version, d, style, is_dark, badge_version, this.color_fixer, 2)}}}`); + out.push(`@media (min-resolution: 200dpi) {${selector}{${generateBadgeCSS(key, version, d, style, is_dark, badge_version, this.color_fixer, 4)}}}`); + } else + out.push(`${selector}{${generateBadgeCSS(key, version, d, style, is_dark, badge_version, this.color_fixer)}}`); } } @@ -784,6 +809,7 @@ export default class Badges extends Module { this.style.delete('twitch-badges'); const badge_version = this.parent.context.get('chat.badges.version'), + use_media = IS_FIREFOX && this.parent.context.get('chat.badges.media-queries'), versioned = CSS_BADGES[badge_version] || {}; const out = []; @@ -795,9 +821,10 @@ export default class Badges extends Module { const versions = this.twitch_badges[key]; for(const version in versions) if ( has(versions, version) ) { - const data = versions[version]; + const data = versions[version], + selector = `.ffz-badge[data-badge="${key}"][data-version="${version}"]`; - out.push(`.ffz-badge[data-badge="${key}"][data-version="${version}"] { + out.push(`${selector} { background-color: transparent; filter: none; ${WEBKIT}mask-image: none; @@ -808,7 +835,16 @@ export default class Badges extends Module { url("${data.image2x}") 2x, url("${data.image4x}") 4x ); - }`) + }`); + + if ( use_media ) { + out.push(`@media (min-resolution: 100dpi) and (max-resolution:199dpi) { ${selector} { + background-image: url("${data.image2x}); + }}`); + out.push(`@media (min-resolution: 200dpi) { ${selector} { + background-image: url("${data.image4x}); + }}`); + } } } diff --git a/src/modules/chat/room.js b/src/modules/chat/room.js index e0fd9b4c..6ce76282 100644 --- a/src/modules/chat/room.js +++ b/src/modules/chat/room.js @@ -6,7 +6,7 @@ import User from './user'; -import {NEW_API, API_SERVER, WEBKIT_CSS as WEBKIT} from 'utilities/constants'; +import {NEW_API, API_SERVER, WEBKIT_CSS as WEBKIT, IS_FIREFOX} from 'utilities/constants'; import {ManagedStyle} from 'utilities/dom'; import {has, SourcedSet, set_equals} from 'utilities/object'; @@ -445,7 +445,8 @@ export default class Room { if ( ! this.badges ) return this.style.delete('badges'); - const out = [], + const use_media = IS_FIREFOX && this.manager.context.get('chat.badges.media-queries'), + out = [], id = this.id; for(const key in this.badges) @@ -454,9 +455,9 @@ export default class Room { for(const version in versions) if ( has(versions, version) ) { const data = versions[version], - rule = `.ffz-badge[data-badge="${key}"][data-version="${version}"]`; + selector = `[data-room-id="${id}"] .ffz-badge[data-badge="${key}"][data-version="${version}"]`; - out.push(`[data-room-id="${id}"] ${rule} { + out.push(`${selector} { background-color: transparent; filter: none; ${WEBKIT}mask-image: none; @@ -470,6 +471,15 @@ export default class Room { url("${data.image4x}") 4x ); }`); + + if ( use_media ) { + out.push(`@media (min-resolution: 100dpi) and (max-resolution:199dpi) { ${selector} { + background-image: url("${data.image2x}"); + }}`); + out.push(`@media (min-resolution: 200dpi) { ${selector} { + background-image: url("${data.image4x}"); + }}`); + } } } diff --git a/src/sites/twitch-twilight/modules/channel_bar.jsx b/src/sites/twitch-twilight/modules/channel_bar.jsx index 58265185..575d2b27 100644 --- a/src/sites/twitch-twilight/modules/channel_bar.jsx +++ b/src/sites/twitch-twilight/modules/channel_bar.jsx @@ -16,6 +16,7 @@ export default class ChannelBar extends Module { this.should_enable = true; + this.inject('i18n'); this.inject('settings'); this.inject('site.css_tweaks'); this.inject('site.fine'); @@ -45,6 +46,11 @@ export default class ChannelBar extends Module { changed: val => this.css_tweaks.toggle('channel-metadata-top', val) }); + this.VideoBar = this.fine.define( + 'video-bar', + n => n.props && n.props.getLastVideoOffset && n.renderTrackedHighlightButton, + ['video', 'user-video'] + ); this.ChannelBar = this.fine.define( 'channel-bar', @@ -56,6 +62,11 @@ export default class ChannelBar extends Module { onEnable() { this.css_tweaks.toggle('channel-metadata-top', this.settings.get('channel.metadata.force-above')); + this.on('i18n:update', () => { + for(const bar of this.VideoBar.instances) + this.updateVideoBar(bar); + }); + this.ChannelBar.on('unmount', this.unmountChannelBar, this); this.ChannelBar.on('mount', this.updateChannelBar, this); this.ChannelBar.on('update', this.updateChannelBar, this); @@ -64,6 +75,35 @@ export default class ChannelBar extends Module { for(const inst of instances) this.updateChannelBar(inst); }); + + + //this.VideoBar.on('unmount', this.unmountVideoBar, this); + this.VideoBar.on('mount', this.updateVideoBar, this); + this.VideoBar.on('update', this.updateVideoBar, this); + + this.VideoBar.ready((cls, instances) => { + for(const inst of instances) + this.updateVideoBar(inst); + }); + + } + + + updateVideoBar(inst) { + const container = this.fine.getChildNode(inst), + timestamp = container && container.querySelector('[data-test-selector="date"]'); + + if ( ! timestamp ) + return; + + const published = get('props.video.publishedAt', inst); + + if ( ! published ) + timestamp.classList.toggle('ffz-tooltip', false); + else { + timestamp.classList.toggle('ffz-tooltip', true); + timestamp.dataset.title = this.i18n.t('video.published-on', 'Published on: {date,date}', {date: published}); + } } diff --git a/src/sites/twitch-twilight/modules/chat/index.js b/src/sites/twitch-twilight/modules/chat/index.js index 81cb7c74..9777f083 100644 --- a/src/sites/twitch-twilight/modules/chat/index.js +++ b/src/sites/twitch-twilight/modules/chat/index.js @@ -252,10 +252,20 @@ export default class ChatHook extends Module { // Settings + this.settings.add('chat.hide-community-highlights', { + default: false, + ui: { + path: 'Chat > Appearance >> Community', + title: 'Hide all Community Highlights from the top of chat.', + component: 'setting-check-box', + description: 'Community Highlights are polls, community gift subs, etc. that float over the top of chat temporarily with no way to close them.' + } + }); + this.settings.add('chat.subs.gift-banner', { default: true, ui: { - path: 'Chat > Appearance >> Subscriptions', + path: 'Chat > Appearance >> Community', title: 'Display a banner at the top of chat when a mass gift sub happens.', component: 'setting-check-box' } @@ -264,7 +274,7 @@ export default class ChatHook extends Module { this.settings.add('chat.community-chest.show', { default: true, ui: { - path: 'Chat > Appearance >> Community Chest', + path: 'Chat > Appearance >> Community', title: 'Display the Community Gift Chest banner.', component: 'setting-check-box' } @@ -684,6 +694,9 @@ export default class ChatHook extends Module { this.css_tweaks.toggle('clickable-mentions', this.chat.context.get('chat.filtering.clickable-mentions')); + this.chat.context.on('changed:chat.hide-community-highlights', val => this.css_tweaks.toggleHide('community-highlights', val)); + + this.css_tweaks.toggleHide('community-highlights', this.chat.context.get('chat.hide-community-highlights')); this.css_tweaks.toggleHide('pinned-cheer', !this.chat.context.get('chat.bits.show-pinned')); this.css_tweaks.toggle('hide-bits', !this.chat.context.get('chat.bits.show')); this.css_tweaks.toggle('chat-rows', this.chat.context.get('chat.lines.alternate')); diff --git a/src/sites/twitch-twilight/modules/css_tweaks/index.js b/src/sites/twitch-twilight/modules/css_tweaks/index.js index ec75a61c..dfff0788 100644 --- a/src/sites/twitch-twilight/modules/css_tweaks/index.js +++ b/src/sites/twitch-twilight/modules/css_tweaks/index.js @@ -21,6 +21,8 @@ const CLASSES = { 'side-offline-channels': '.side-nav-card__link[href*="/videos/"],.side-nav-card[href*="/videos/"]', 'side-rerun-channels': '.side-nav .ffz--side-nav-card-rerun', + 'community-highlights': '.community-highlight-stack__card', + 'prime-offers': '.top-nav__prime', 'player-ext': '.video-player .extension-taskbar,.video-player .extension-container,.video-player .extensions-dock__layout,.video-player .extensions-notifications,.video-player .extensions-video-overlay-size-container,.video-player .extensions-dock__layout', diff --git a/src/sites/twitch-twilight/modules/css_tweaks/styles/portrait.scss b/src/sites/twitch-twilight/modules/css_tweaks/styles/portrait.scss index 894aa62f..e713f33b 100644 --- a/src/sites/twitch-twilight/modules/css_tweaks/styles/portrait.scss +++ b/src/sites/twitch-twilight/modules/css_tweaks/styles/portrait.scss @@ -6,6 +6,10 @@ & > .tw-flex-column > .tw-full-height { .ffz--portrait-invert & { + position: absolute; + left: 0; + right: 0; + bottom: 0; top: var(--ffz-chat-height) !important; }