diff --git a/changelog.html b/changelog.html index 0ea176a7..28177ffd 100644 --- a/changelog.html +++ b/changelog.html @@ -1,3 +1,53 @@ +
system-msg
tags that contain template strings or other nonsense that Twitch, for some reason, isn't just putting in the message itself and is making the client reconstruct from tags.has_prefix
for emote sets and prefix_length
for emote sets and emotes.has_prefix
for emote sets and prefix_length
for emote sets and emotes.Case insensitive. One name per line.", + old_val, + function(new_val) { + if ( new_val === null || new_val === undefined ) + return; + + // Split them up. + var vals = new_val.toLowerCase().trim().split(/\s*\n\s*/g), + i = vals.length; + + while(i--) + if ( vals[i].length === 0 ) + vals.splice(i,1); + + f.settings.set("key_users", vals); + }, + 600, input); + } + }; + + FFZ.settings_info.keywords = { type: "button", value: [], diff --git a/src/ember/moderation-card.js b/src/ember/moderation-card.js index e14ceca2..e2c2d6dc 100644 --- a/src/ember/moderation-card.js +++ b/src/ember/moderation-card.js @@ -1611,9 +1611,9 @@ FFZ.prototype._build_mod_card_history = function(msg, modcard, show_from, ts_cli jQuery('img.emoticon', l_el).click(function(e) { f._click_emote(this, e) }); if ( modcard ) { - modcard.get('cardInfo.user.id') !== msg.from && jQuery('span.from', l_el).click(function(e) { + modcard.get('cardInfo.user.id') !== msg.from && jQuery('.from', l_el).click(function(e) { var el = modcard.get('element'); - el && f._roomv && f._roomv.get('room.id') === msg.room && f._roomv.actions.showModCard.call(f._roomv, { + el && f._roomv && f._roomv.get('room.id') === msg.room && f._roomv.actions.showModOverlay.call(f._roomv, { sender: msg.from, top: parseInt(el.style.top), left: parseInt(el.style.left) diff --git a/src/ember/room.js b/src/ember/room.js index 945018c6..5ae8df59 100644 --- a/src/ember/room.js +++ b/src/ember/room.js @@ -1330,6 +1330,18 @@ FFZ.prototype._modify_room = function(room) { }, addFriendsWatchingMessage: function(msg) { + if ( f.settings.friend_notifications && ! document.hasFocus() ) + f.show_notification( + msg.replace(/ *VoHiYo$/g, ''), + document.title, + "ffz_watching_notice", + (this.settings.notification_timeout*1000), + function() { + window.focus(); + }, + null, + 'https://static-cdn.jtvnw.net/emoticons/v1/81274/3.0'); + this.addMessage({ style: 'admin' + (f.has_bttv_6 ? '' : ' friend-watching'), message: msg, diff --git a/src/ember/sidebar.js b/src/ember/sidebar.js index 3d6a80e0..1d5c99fb 100644 --- a/src/ember/sidebar.js +++ b/src/ember/sidebar.js @@ -184,6 +184,71 @@ FFZ.settings_info.sidebar_disable_friends = { }; +var TWITCH_NAV_COLOR = "#4b367c", + TWITCH_NAV_RGB = FFZ.Color.RGBA.fromCSS(TWITCH_NAV_COLOR), + TWITCH_NAV_Luv = TWITCH_NAV_RGB.toLUVA(); + +FFZ.settings_info.top_nav_color = { + type: "button", + value: "#4b367c", + + category: "Sidebar", + no_mobile: true, + + name: "Top Navigation Color", + help: "Set a custom background color for the top navigation bar.", + + on_update: function(val) { + var process = true; + val = val.trim(); + + if ( val.charAt(0) === '!' ) { + process = false; + val = val.substr(1).trimLeft(); + } + + var color = val && FFZ.Color.RGBA.fromCSS(val), + color_luv = color && color.toLUVA(); + + if ( ! val || process && ! color ) + return utils.update_css(this._theme_style, 'top-nav-color'); + + else if ( process && color_luv.l > TWITCH_NAV_Luv.l ) { + color = color_luv._l(TWITCH_NAV_Luv.l).toRGBA(); + val = color.toCSS(); + } + + var out = '.top-nav__menu,.top-nav__drawer-anchor,.top-nav__logo{background-color:' + val + '}'; + + if ( color.luminance() > 0.2 ) { + out += '.top-nav .notification-center__icon svg,.top-nav .prime-logo-crown.prime-logo-crown--white svg,.top-nav__logo svg path, .top-nav__overflow svg path{fill: #000}' + + '.top-nav__user-card:after{border-top-color:#000}' + + '.top-nav__user-status,.top-nav__nav-link{color: #111}' + + '.top-nav #user_display_name,.top-nav__nav-link:hover{color: #000}'; + } else + out += '.top-nav__nav-link{color: #d7d7d7}'; + + utils.update_css(this._theme_style, 'top-nav-color', out); + }, + + method: function() { + var f = this; + utils.prompt( + "Top Navigation Color", + "Please enter a custom color for the top navigation bar. This supports any valid CSS color or color name.
Examples: red
, orange
, #333
, rgb(255,127,127)
Note: Colors will be darkened by default. To prevent a color being darkened, please start your input with an exclamation mark. Example: !orange
",
+ this.settings.top_nav_color,
+ function(new_val) {
+ if ( new_val === null || new_val === undefined )
+ return;
+
+ new_val = new_val.trim();
+ f.settings.set("top_nav_color", new_val);
+ }
+ )
+ }
+}
+
+
/*FFZ.settings_info.sidebar_start_open = {
type: "boolean",
value: false,
@@ -213,6 +278,13 @@ FFZ.settings_info.sidebar_directly_to_followed_channels = {
// --------------------
FFZ.prototype.setup_sidebar = function() {
+ var s = this._theme_style = utils.createElement('style');
+ s.id = 'ffz-theme-style';
+ s.type = 'text/css';
+ document.head.appendChild(s);
+
+ FFZ.settings_info.top_nav_color.on_update.call(this, this.settings.top_nav_color);
+
// CSS to Hide Stuff
utils.toggle_cls('ffz-hide-promoted-games')(this.settings.sidebar_hide_promoted_games);
utils.toggle_cls('ffz-hide-recommended-channels')(this.settings.sidebar_hide_recommended_channels);
@@ -259,8 +331,11 @@ FFZ.prototype.setup_sidebar = function() {
// Navigation Component
- this.update_views('component:twitch-navigation', this.modify_navigation);
+ var f = this;
+ this.update_views('component:twitch-navigation', function(x) { return f.modify_navigation(x, false) });
+ this.update_views('component:top-nav', function(x) { return f.modify_navigation(x, true) });
this.update_views('component:recommended-channels', this.modify_recommended_channels);
+ this.update_views('component:social-column/followed-channel', this.modify_social_followed_channel)
// Navigation Service
/*var NavService = utils.ember_lookup('service:navigation');
@@ -296,7 +371,66 @@ FFZ.prototype.modify_recommended_channels = function(component) {
}
-FFZ.prototype.modify_navigation = function(component) {
+FFZ._sc_followed_tooltip_id = 0;
+
+FFZ.prototype.modify_social_followed_channel = function(component) {
+ var f = this;
+ utils.ember_reopen_view(component, {
+ ffz_init: function() {
+ var t = this,
+ el = this.get('element'),
+ card = jQuery('.sc-card', el),
+ data = card && card.data('tipsy');
+
+ if ( ! data || ! data.options )
+ return;
+
+ data.options.html = true;
+ data.options.gravity = utils.tooltip_placement(constants.TOOLTIP_DISTANCE, 'w');
+ data.options.title = function(el) {
+ var old_text = t.get('tooltipText');
+ if ( ! f.settings.following_count )
+ return utils.sanitize(old_text);
+
+ var tt_id = FFZ._sc_followed_tooltip_id++;
+ utils.api.get("streams/" + t.get('stream.id'), null, {version: 5}).then(function(data) {
+ var el = document.querySelector('#ffz-sc-tooltip-' + tt_id);
+ if ( ! el || ! data || ! data.stream )
+ return;
+
+ var channel = data.stream.channel,
+ is_spoilered = f.settings.spoiler_games.indexOf(channel.game && channel.game.toLowerCase()) !== -1,
+
+ up_since = f.settings.stream_uptime && data.stream.created_at && utils.parse_date(data.stream.created_at),
+ now = Date.now() - (f._ws_server_offset || 0),
+ uptime = up_since && (Math.floor((now - up_since.getTime()) / 60000) * 60) || 0;
+
+ var cl = el.parentElement.parentElement.classList;
+ cl.add('ffz-wide-tip');
+ cl.add('ffz-follow-tip');
+
+ el.innerHTML = '' +
+ '' + utils.sanitize(channel.status) + '