mirror of
https://github.com/FrankerFaceZ/FrankerFaceZ.git
synced 2025-08-02 16:08:31 +00:00
3.5.58 to 3.5.62. Added enhanced following controls to the profile page. New Conversations options. Tweaked conversation styles.
This commit is contained in:
parent
c40b3ba337
commit
4f6dcc9999
15 changed files with 3017 additions and 38 deletions
5
dark.css
5
dark.css
|
@ -1036,4 +1036,7 @@
|
|||
.ffz-dark .conversation-window.has-focus .conversation-input-actions .button,
|
||||
.ffz-dark .conversation-window.has-focus .conversation-input-actions .follow-button:not(.ember-follow) .follow {
|
||||
background-color: #6441a5
|
||||
}
|
||||
}
|
||||
|
||||
.ffz-dark .conversation-window .new-message-divider span { background: transparent; }
|
||||
.ffz-dark .conversation-window .new-message-divider:after { display: none; }
|
|
@ -570,19 +570,7 @@ FFZ.prototype._update_colors = function(darkness_only) {
|
|||
|
||||
this._color_old_darkness = is_dark;
|
||||
|
||||
var colored_bits = document.querySelectorAll('.chat-line .has-color');
|
||||
for(var i=0, l=colored_bits.length; i < l; i++) {
|
||||
var bit = colored_bits[i],
|
||||
color = bit.getAttribute('data-color'),
|
||||
colors = color && this._handle_color(color);
|
||||
|
||||
if ( ! colors )
|
||||
continue;
|
||||
|
||||
bit.style.color = is_dark ? colors[1] : colors[0];
|
||||
}
|
||||
|
||||
colored_bits = document.querySelectorAll('.conversation-chat-line .has-color');
|
||||
var colored_bits = document.querySelectorAll('.has-color');
|
||||
for(var i=0, l=colored_bits.length; i < l; i++) {
|
||||
var bit = colored_bits[i],
|
||||
color = bit.getAttribute('data-color'),
|
||||
|
|
|
@ -77,6 +77,7 @@ module.exports = {
|
|||
CLOCK: '<svg class="svg-glyph_views ffz-svg svg-clock" height="16px" version="1.1" viewBox="0 0 16 16" width="16px" x="0px" y="0px"><path fill-rule="evenodd" clip-rule="evenodd" fill="#888888" d="M8,15c-3.866,0-7-3.134-7-7s3.134-7,7-7s7,3.134,7,7 S11.866,15,8,15z M8,3C5.238,3,3,5.238,3,8s2.238,5,5,5s5-2.238,5-5S10.762,3,8,3z M7.293,8.707L7,8l1-4l0.902,3.607L11,11 L7.293,8.707z"/></svg>',
|
||||
GEAR: '<svg class="svg-gear" height="16px" version="1.1" viewBox="0 0 16 16" width="16px" x="0px" y="0px"><path clip-rule="evenodd" d="M15,7v2h-2.115c-0.125,0.615-0.354,1.215-0.713,1.758l1.484,1.484l-1.414,1.414l-1.484-1.484C10.215,12.531,9.615,12.76,9,12.885V15H7v-2.12c-0.614-0.126-1.21-0.356-1.751-0.714l-1.491,1.49l-1.414-1.414l1.491-1.49C3.477,10.211,3.247,9.613,3.12,9H1V7h2.116C3.24,6.384,3.469,5.785,3.829,5.242L2.343,3.757l1.414-1.414l1.485,1.485C5.785,3.469,6.384,3.24,7,3.115V1h2v2.12c0.613,0.126,1.211,0.356,1.752,0.714l1.49-1.491l1.414,1.414l-1.49,1.492C12.523,5.79,12.754,6.387,12.88,7H15z M8,6C6.896,6,6,6.896,6,8s0.896,2,2,2s2-0.896,2-2S9.104,6,8,6z" fill-rule="evenodd"></path></svg>',
|
||||
HEART: '<svg class="svg-heart" height="16px" version="1.1" viewBox="0 0 16 16" width="16px" x="0px" y="0px"><path clip-rule="evenodd" d="M8,13.5L1.5,7V4l2-2h3L8,3.5L9.5,2h3l2,2v3L8,13.5z" fill-rule="evenodd"></path></svg>',
|
||||
UNHEART: '<svg class="svg-unheart" height="16px" version="1.1" viewbox="0 0 16 16" width="16px" x="0px" y="0px"><path clip-rule="evenodd" d="M1,9V7h14v2H1z M1,4l2-2h3l2,2l2-2h3l2,2v2H1V4z M8,14l-4.667-4h9.333L8,14z" fill-rule="evenodd"></path></svg>',
|
||||
EMOTE: '<svg class="svg-emote" height="16px" version="1.1" viewBox="0 0 18 18" width="16px" x="0px" y="0px"><path clip-rule="evenodd" d="M9,18c-4.971,0-9-4.029-9-9s4.029-9,9-9s9,4.029,9,9S13.971,18,9,18z M14,4.111V4h-0.111C12.627,2.766,10.904,2,9,2C7.095,2,5.373,2.766,4.111,4H4v0.111C2.766,5.373,2,7.096,2,9s0.766,3.627,2,4.889V14l0.05-0.051C5.317,15.217,7.067,16,9,16c1.934,0,3.684-0.783,4.949-2.051L14,14v-0.111c1.234-1.262,2-2.984,2-4.889S15.234,5.373,14,4.111zM11,6h2v4h-2V6z M12.535,12.535C11.631,13.44,10.381,14,9,14s-2.631-0.56-3.536-1.465l0.707-0.707C6.896,12.553,7.896,13,9,13s2.104-0.447,2.828-1.172L12.535,12.535z M5,6h2v4H5V6z" fill-rule="evenodd"></path></svg>',
|
||||
STAR: '<svg class="svg-star" height="16px" version="1.1" viewbox="0 0 16 16" width="16px" x="0px" y="0px"><path clip-rule="evenodd" d="M15,6l-4.041,2.694L13,14l-5-3.333L3,14l2.041-5.306L1,6h5.077L8,1l1.924,5H15z" fill-rule="evenodd"></path></svg>',
|
||||
CLOSE: '<svg class="svg-close_small" height="16px" version="1.1" viewbox="0 0 16 16" width="16px" x="0px" y="0px"><path clip-rule="evenodd" d="M12.657,4.757L9.414,8l3.243,3.242l-1.415,1.415L8,9.414l-3.243,3.243l-1.414-1.415L6.586,8L3.343,4.757l1.414-1.414L8,6.586l3.242-3.243L12.657,4.757z" fill-rule="evenodd"></path></svg>',
|
||||
|
|
252
src/ember/conversations.js
Normal file
252
src/ember/conversations.js
Normal file
|
@ -0,0 +1,252 @@
|
|||
var FFZ = window.FrankerFaceZ,
|
||||
utils = require('../utils'),
|
||||
constants = require('../constants');
|
||||
|
||||
|
||||
// ---------------
|
||||
// Settings
|
||||
// ---------------
|
||||
|
||||
FFZ.settings_info.conv_title_clickable = {
|
||||
type: "boolean",
|
||||
value: false,
|
||||
no_mobile: true,
|
||||
|
||||
category: "Conversations",
|
||||
name: "Clickable Header Name",
|
||||
help: "Make the conversation header a link that takes you to that person's page.",
|
||||
on_update: function(val) {
|
||||
document.body.classList.toggle('ffz-conv-title-clickable', val);
|
||||
}
|
||||
};
|
||||
|
||||
FFZ.settings_info.conv_focus_on_click = {
|
||||
type: "boolean",
|
||||
value: false,
|
||||
no_mobile: true,
|
||||
|
||||
category: "Conversations",
|
||||
name: "Focus Input on Click",
|
||||
help: "Focus on a conversation's input box when you click it."
|
||||
};
|
||||
|
||||
FFZ.settings_info.top_conversations = {
|
||||
type: "boolean",
|
||||
value: false,
|
||||
no_mobile: true,
|
||||
|
||||
category: "Conversations",
|
||||
name: "Position on Top",
|
||||
help: "Display the new conversation-style whisper UI at the top of the window instead of the bottom.",
|
||||
on_update: function(val) {
|
||||
document.body.classList.toggle('ffz-top-conversations', val);
|
||||
}
|
||||
};
|
||||
|
||||
FFZ.settings_info.conv_beta_enable = {
|
||||
type: "boolean",
|
||||
value: false,
|
||||
no_mobile: true,
|
||||
|
||||
category: "Conversations",
|
||||
name: "Enable Conversations",
|
||||
help: "Twitch hasn't enabled them yet, but they're in the code for testing. Try them out!",
|
||||
on_update: function(val) {
|
||||
App.__container__.lookup('route:application').controller.set('isConversationsEnabled', val);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// ---------------
|
||||
// Initialization
|
||||
// ---------------
|
||||
|
||||
FFZ.prototype.setup_conversations = function() {
|
||||
document.body.classList.toggle('ffz-top-conversations', this.settings.top_conversations);
|
||||
document.body.classList.toggle('ffz-conv-title-clickable', this.settings.conv_title_clickable);;
|
||||
|
||||
if ( this.settings.conv_beta_enable )
|
||||
App.__container__.lookup('route:application').controller.set('isConversationsEnabled', true);
|
||||
|
||||
this.log("Hooking the Ember Conversation Window component.");
|
||||
var ConvWindow = App.__container__.resolve('component:conversation-window');
|
||||
if ( ConvWindow )
|
||||
this._modify_conversation_window(ConvWindow);
|
||||
|
||||
|
||||
this.log("Hooking the Ember Conversation Line component.");
|
||||
var ConvLine = App.__container__.resolve('component:conversation-line');
|
||||
if ( ConvLine )
|
||||
this._modify_conversation_line(ConvLine);
|
||||
}
|
||||
|
||||
|
||||
FFZ.prototype._modify_conversation_window = function(component) {
|
||||
var f = this,
|
||||
|
||||
Layout = App.__container__.lookup('controller:layout'),
|
||||
Settings = App.__container__.lookup('controller:settings');
|
||||
|
||||
component.reopen({
|
||||
onConversationClick: Ember.on('click', function() {
|
||||
this.markConversationRead();
|
||||
if ( f.settings.conv_focus_on_click )
|
||||
this.$(".conversation-input-bar textarea").focus();
|
||||
}),
|
||||
|
||||
headerBadges: Ember.computed("conversation.participants", "currentUsername", function() {
|
||||
var e = this.get("conversation.participants").rejectBy("username", this.get("currentUsername")).objectAt(0),
|
||||
badges = {},
|
||||
|
||||
ut = e.get("userType");
|
||||
|
||||
if ( ut === "staff" )
|
||||
badges[0] = {classes: 'badge staff', title: 'Staff'};
|
||||
else if ( ut === 'admin' )
|
||||
badges[0] = {classes: 'badge admin', title: 'Admin'};
|
||||
else if ( ut === 'global_mod' )
|
||||
badges[0] = {classes: 'badge global-moderator', title: 'Global Moderator'};
|
||||
|
||||
if ( e.get('hasTurbo') )
|
||||
badges[15] = {classes: 'badge turbo', title: 'Turbo'}
|
||||
|
||||
// FFZ Badges
|
||||
var data = f.users[e.get('username')];
|
||||
if ( data && data.badges ) {
|
||||
for(var slot in data.badges) {
|
||||
if ( ! data.badges.hasOwnProperty(slot) )
|
||||
continue;
|
||||
|
||||
var badge = data.badges[slot],
|
||||
full_badge = f.badges[badge.id] || {},
|
||||
old_badge = badges[slot];
|
||||
|
||||
if ( full_badge.visible !== undefined ) {
|
||||
var visible = full_badge.visible;
|
||||
if ( typeof visible === "function" )
|
||||
try {
|
||||
visible = visible.bind(f)(null, e.get('username'), null, badges);
|
||||
} catch(err) {
|
||||
f.error("badge " + badge.id + " visible: " + err);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( ! visible )
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( old_badge ) {
|
||||
var replaces = badge.hasOwnProperty('replaces') ? badge.replaces : full_badge.replaces;
|
||||
if ( ! replaces )
|
||||
continue;
|
||||
|
||||
old_badge.klass = 'badge ffz-badge-' + badge.id;
|
||||
old_badge.title += ', ' + (badge.title || full_badge.title);
|
||||
continue;
|
||||
}
|
||||
|
||||
badges[slot] = {
|
||||
classes: 'badge ffz-badge-' + badge.id,
|
||||
title: badge.title || full_badge.title
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var out = [];
|
||||
for(var slot in badges)
|
||||
out.push(badges[slot]);
|
||||
|
||||
return out;
|
||||
}),
|
||||
|
||||
didInsertElement: function() {
|
||||
var el = this.get('element'),
|
||||
header = el && el.querySelector('.conversation-header'),
|
||||
header_name = header && header.querySelector('.conversation-header-name'),
|
||||
|
||||
new_header_name = document.createElement('span'),
|
||||
|
||||
raw_color = this.get('otherUser.color'),
|
||||
colors = raw_color && f._handle_color(raw_color),
|
||||
|
||||
is_dark = (Layout && Layout.get('isTheatreMode')) || f.settings.dark_twitch;
|
||||
|
||||
if ( header_name ) {
|
||||
new_header_name.className = 'conversation-header-name';
|
||||
new_header_name.textContent = header_name.textContent;
|
||||
header.insertBefore(new_header_name, header_name);
|
||||
|
||||
if ( raw_color ) {
|
||||
header_name.style.color = (is_dark ? colors[1] : colors[0]);
|
||||
header_name.classList.add('has-color');
|
||||
header_name.setAttribute('data-color', raw_color);
|
||||
|
||||
new_header_name.style.color = (is_dark ? colors[1] : colors[0]);
|
||||
new_header_name.classList.add('has-color');
|
||||
new_header_name.setAttribute('data-color', raw_color);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
FFZ.prototype._modify_conversation_line = function(component) {
|
||||
var f = this,
|
||||
|
||||
Layout = App.__container__.lookup('controller:layout'),
|
||||
Settings = App.__container__.lookup('controller:settings');
|
||||
|
||||
component.reopen({
|
||||
tokenizedMessage: function() {
|
||||
try {
|
||||
return f.tokenize_conversation_line(this.get('message'));
|
||||
} catch(err) {
|
||||
f.error("convo-line tokenizedMessage: " + err);
|
||||
return this._super();
|
||||
}
|
||||
|
||||
}.property("message", "currentUsername"),
|
||||
|
||||
click: function(e) {
|
||||
if ( e.target && e.target.classList.contains('deleted-link') )
|
||||
return f._deleted_link_click.bind(e.target)(e);
|
||||
|
||||
if ( f._click_emote(e.target, e) )
|
||||
return;
|
||||
|
||||
return this._super(e);
|
||||
},
|
||||
|
||||
render: function(e) {
|
||||
var user = this.get('message.from.username'),
|
||||
raw_color = this.get('message.from.color'),
|
||||
colors = raw_color && f._handle_color(raw_color),
|
||||
|
||||
is_dark = (Layout && Layout.get('isTheatreMode')) || f.settings.dark_twitch;
|
||||
|
||||
e.push('<div class="indicator"></div>');
|
||||
|
||||
var alias = f.aliases[user],
|
||||
name = this.get('message.from.displayName') || (user && user.capitalize()) || "unknown user",
|
||||
style = colors && 'color:' + (is_dark ? colors[1] : colors[0]),
|
||||
colored = style ? ' has-color' : '';
|
||||
|
||||
if ( alias )
|
||||
e.push('<span class="from ffz-alias tooltip' + colored + '" style="' + style + (colors ? '" data-color="' + raw_color : '') + '" title="' + utils.sanitize(name) + '">' + utils.sanitize(alias) + '</span>');
|
||||
else
|
||||
e.push('<span class="from' + colored + '" style="' + style + (colors ? '" data-color="' + raw_color : '') + '">' + utils.sanitize(name) + '</span>');
|
||||
|
||||
e.push('<span class="colon">:</span> ');
|
||||
|
||||
if ( ! this.get('isActionMessage') ) {
|
||||
style = '';
|
||||
colored = '';
|
||||
}
|
||||
|
||||
e.push('<span class="message' + colored + '" style="' + style + (colors ? '" data-color="' + raw_color : '') + '">');
|
||||
e.push(f.render_tokens(this.get('tokenizedMessage'), true));
|
||||
e.push('</span>');
|
||||
}
|
||||
});
|
||||
}
|
|
@ -45,8 +45,12 @@ FFZ.prototype.setup_directory = function() {
|
|||
// Initialize existing views.
|
||||
for(var key in Ember.View.views) {
|
||||
var view = Ember.View.views[key];
|
||||
if ( view instanceof ChannelView || view instanceof CreativeChannel || view instanceof CSGOChannel || view instanceof HostView )
|
||||
view.ffzInit();
|
||||
try {
|
||||
if ( (ChannelView && view instanceof ChannelView) || (CreativeChannel && view instanceof CreativeChannel) || (CSGOChannel && view instanceof CSGOChannel) || (HostView && view instanceof HostView) )
|
||||
view.ffzInit();
|
||||
} catch(err) {
|
||||
this.error("Directory Setup: " + err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
318
src/ember/following.js
Normal file
318
src/ember/following.js
Normal file
|
@ -0,0 +1,318 @@
|
|||
var FFZ = window.FrankerFaceZ,
|
||||
utils = require('../utils'),
|
||||
constants = require('../constants');
|
||||
|
||||
|
||||
// --------------------
|
||||
// Settings
|
||||
// --------------------
|
||||
|
||||
FFZ.settings_info.enhance_profile_following = {
|
||||
type: "boolean",
|
||||
value: true,
|
||||
|
||||
category: "Appearance",
|
||||
name: "Enhanced Following Control",
|
||||
help: "Display additional controls on your own profile's Following tab to make management easier."
|
||||
}
|
||||
|
||||
|
||||
// --------------------
|
||||
// Initialization
|
||||
// --------------------
|
||||
|
||||
FFZ.prototype.setup_profile_following = function() {
|
||||
if ( ! window.App )
|
||||
return;
|
||||
|
||||
var f = this;
|
||||
|
||||
// Build our is-following cache.
|
||||
this._following_cache = {};
|
||||
|
||||
// First, we need to hook the model. This is what we'll use to grab the following notification state,
|
||||
// rather than making potentially hundreds of API requests.
|
||||
var Following = App.__container__.resolve('model:kraken-channel-following');
|
||||
if ( ! Following )
|
||||
return;
|
||||
|
||||
this._hook_following(Following);
|
||||
|
||||
// Also try hooking that other model.
|
||||
var Notification = App.__container__.resolve('model:notification');
|
||||
if ( Notification )
|
||||
this._hook_following(Notification, true);
|
||||
|
||||
|
||||
// Now, we need to edit the profile Following view itself.
|
||||
var ProfileView = App.__container__.resolve('view:channel/following');
|
||||
if ( ! ProfileView )
|
||||
return;
|
||||
|
||||
ProfileView.reopen({
|
||||
didInsertElement: function() {
|
||||
this._super();
|
||||
try {
|
||||
this.ffzInit();
|
||||
} catch(err) {
|
||||
f.error("ProfileView ffzInit: " + err);
|
||||
}
|
||||
},
|
||||
|
||||
willClearRender: function() {
|
||||
try {
|
||||
this.ffzTeardown();
|
||||
} catch(err) {
|
||||
f.error("ProvileView ffzTeardown: " + err);
|
||||
}
|
||||
this._super();
|
||||
},
|
||||
|
||||
ffzInit: function() {
|
||||
// Only process our own profile following page.
|
||||
var user = f.get_user();
|
||||
if ( ! f.settings.enhance_profile_following || ! user || ! user.login === this.get('context.id') )
|
||||
return;
|
||||
|
||||
var el = this.get('element'),
|
||||
users = el && el.querySelectorAll('.user.item');
|
||||
|
||||
el.classList.add('ffz-enhanced-following');
|
||||
|
||||
var had_data = true;
|
||||
|
||||
if ( users && users.length )
|
||||
for(var i=0; i < users.length; i++)
|
||||
had_data = this.ffzProcessUser(users[i]) && had_data;
|
||||
else
|
||||
had_data = false;
|
||||
|
||||
if ( ! had_data ) {
|
||||
// Force a refresh.
|
||||
f.log("Forcing a refresh of user following data.");
|
||||
var following = this.get('context.following'),
|
||||
refresher = function() {
|
||||
if ( following.get('isLoading') )
|
||||
setTimeout(refresher, 25);
|
||||
|
||||
following.clear();
|
||||
following.load();
|
||||
}
|
||||
|
||||
// We use this weird function to prevent trying to load twice mucking things up.
|
||||
setTimeout(refresher);
|
||||
}
|
||||
|
||||
// Watch for new ones the bad way.
|
||||
if ( ! this._ffz_observer ) {
|
||||
var t = this;
|
||||
var observer = this._ffz_observer = new MutationObserver(function(mutations) {
|
||||
for(var i=0; i < mutations.length; i++) {
|
||||
var mutation = mutations[i];
|
||||
if ( mutation.type !== "childList" )
|
||||
continue;
|
||||
|
||||
for(var x=0; x < mutation.addedNodes.length; x++) {
|
||||
var added = mutation.addedNodes[x];
|
||||
if ( added.nodeType !== added.ELEMENT_NODE || added.tagName !== "DIV" )
|
||||
continue;
|
||||
|
||||
// Is it an ember-view? Check its kids.
|
||||
if ( added.classList.contains('ember-view') ) {
|
||||
var users = added.querySelectorAll('.user.item');
|
||||
if ( users )
|
||||
for(var y=0; y < users.length; y++)
|
||||
t.ffzProcessUser(users[y]);
|
||||
|
||||
} else if ( added.classList.contains('user') )
|
||||
t.ffzProcessUser(added);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
observer.observe(el, {
|
||||
childList: true,
|
||||
subtree: true
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
ffzTeardown: function() {
|
||||
if ( this._ffz_observer ) {
|
||||
this._ffz_observer.disconnect();
|
||||
this._ffz_observer = null;
|
||||
}
|
||||
},
|
||||
|
||||
ffzProcessUser: function(user) {
|
||||
if ( user.classList.contains('ffz-processed') )
|
||||
return true;
|
||||
|
||||
var link = user.querySelector('a'),
|
||||
link_parts = link && link.href.split("/"),
|
||||
user_id = link_parts && link_parts[3],
|
||||
data = f._following_cache[user_id],
|
||||
t_el = document.createElement('div');
|
||||
|
||||
user.classList.add('ffz-processed');
|
||||
if ( ! data )
|
||||
return false;
|
||||
|
||||
t_el.className = 'overlay_info length';
|
||||
jQuery(t_el).tipsy({html: true});
|
||||
|
||||
var age = data[0] ? Math.floor((Date.now() - data[0].getTime()) / 1000) : 0;
|
||||
if ( age ) {
|
||||
t_el.innerHTML = constants.CLOCK + ' ' + utils.human_time(age, 10);
|
||||
t_el.setAttribute('original-title', 'Following Since: <nobr>' + data[0].toLocaleString() + '</nobr>');
|
||||
} else
|
||||
t_el.style.display = 'none';
|
||||
|
||||
user.appendChild(t_el);
|
||||
|
||||
var actions = document.createElement('div'),
|
||||
follow = document.createElement('button'),
|
||||
notif = document.createElement('button'),
|
||||
|
||||
update_follow = function() {
|
||||
data = f._following_cache[user_id];
|
||||
user.classList.toggle('followed', data);
|
||||
follow.innerHTML = constants.HEART + constants.UNHEART + '<span> Follow</span>';
|
||||
|
||||
if ( t_el ) {
|
||||
var age = data && data[0] ? Math.floor((Date.now() - data[0].getTime()) / 1000) : undefined;
|
||||
if ( age !== undefined ) {
|
||||
t_el.innerHTML = constants.CLOCK + ' ' + (age < 60 ? 'now' : utils.human_time(age, 10));
|
||||
t_el.setAttribute('original-title', 'Following Since: <nobr>' + data[0].toLocaleString() + '</nobr>');
|
||||
t_el.style.display = '';
|
||||
} else {
|
||||
t_el.style.display = 'none';
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
update_notif = function() {
|
||||
data = f._following_cache[user_id];
|
||||
notif.classList.toggle('notifications-on', data && data[1]);
|
||||
notif.textContent = 'Notification ' + (data && data[1] ? 'On' : 'Off');
|
||||
};
|
||||
|
||||
actions.className = 'actions';
|
||||
|
||||
follow.className = 'button follow';
|
||||
notif.className = 'button notifications';
|
||||
|
||||
update_follow();
|
||||
update_notif();
|
||||
|
||||
follow.addEventListener('click', function() {
|
||||
var was_following = !!data;
|
||||
|
||||
follow.disabled = true;
|
||||
notif.disabled = true;
|
||||
follow.textContent = 'Updating';
|
||||
|
||||
(was_following ?
|
||||
Twitch.api.del("users/:login/follows/channels/" + user_id) :
|
||||
Twitch.api.put("users/:login/follows/channels/" + user_id, {notifications: false}))
|
||||
.done(function() {
|
||||
data = f._following_cache[user_id] = was_following ? null : [new Date(), false];
|
||||
})
|
||||
.always(function() {
|
||||
update_follow();
|
||||
update_notif();
|
||||
follow.disabled = false;
|
||||
notif.disabled = false;
|
||||
})
|
||||
});
|
||||
|
||||
notif.addEventListener('click', function() {
|
||||
var was_following = data[1];
|
||||
|
||||
follow.disabled = true;
|
||||
notif.disabled = true;
|
||||
notif.textContent = 'Updating';
|
||||
|
||||
Twitch.api.put("users/:login/follows/channels/" + user_id, {notifications: !was_following})
|
||||
.done(function() {
|
||||
data[1] = ! was_following;
|
||||
})
|
||||
.always(function() {
|
||||
update_notif();
|
||||
follow.disabled = false;
|
||||
notif.disabled = false;
|
||||
});
|
||||
});
|
||||
|
||||
actions.appendChild(follow);
|
||||
actions.appendChild(notif);
|
||||
user.appendChild(actions);
|
||||
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
// Now, rebuild any views.
|
||||
try {
|
||||
ProfileView.create().destroy();
|
||||
} catch(err) { }
|
||||
|
||||
for(var key in Ember.View.views) {
|
||||
var view = Ember.View.views[key];
|
||||
if ( ! view || !(view instanceof ProfileView) )
|
||||
continue;
|
||||
|
||||
this.log("Manually updating existing Following View.", view);
|
||||
try {
|
||||
var following = view.get('context.following');
|
||||
this._hook_following(following);
|
||||
} catch(err) {
|
||||
this.error("setup: view:channel/following: model hook: " + err);
|
||||
}
|
||||
|
||||
try {
|
||||
view.ffzInit();
|
||||
} catch(err) {
|
||||
this.error("setup: view:channel/following: " + err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
FFZ.prototype._hook_following = function(Following) {
|
||||
var f = this;
|
||||
Following.reopen({
|
||||
apiLoad: function(e) {
|
||||
var user = f.get_user(),
|
||||
channel_id = this.get('id'),
|
||||
t = this;
|
||||
|
||||
if ( ! user || user.login !== channel_id )
|
||||
return this._super(e);
|
||||
|
||||
return new RSVP.Promise(function(success, fail) {
|
||||
t._super(e).then(function(data) {
|
||||
if ( data && data.follows ) {
|
||||
var now = Date.now();
|
||||
for(var i=0; i < data.follows.length; i++) {
|
||||
var follow = data.follows[i];
|
||||
if ( ! follow || ! follow.channel || ! follow.channel.name ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( follow.channel.display_name )
|
||||
FFZ.capitalization[follow.channel.name] = [follow.channel.display_name, now];
|
||||
|
||||
f._following_cache[follow.channel.name] = [follow.created_at ? utils.parse_date(follow.created_at) : null, follow.notifications || false];
|
||||
}
|
||||
}
|
||||
|
||||
success(data);
|
||||
|
||||
}, function(err) {
|
||||
fail(err);
|
||||
})
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
|
@ -549,19 +549,11 @@ FFZ.prototype.setup_line = function() {
|
|||
this._modify_line(Whisper);
|
||||
|
||||
this.log("Hooking the Ember Message Line component.");
|
||||
|
||||
var Line = App.__container__.resolve('component:message-line');
|
||||
|
||||
if ( Line )
|
||||
this._modify_line(Line);
|
||||
|
||||
this.log("Hooking the Ember Conversation Line component.");
|
||||
var Conversation = App.__container__.resolve('component:conversation-line');
|
||||
|
||||
if ( Conversation )
|
||||
this._modify_conversation_line(Conversation);
|
||||
|
||||
|
||||
// Store the capitalization of our own name.
|
||||
var user = this.get_user();
|
||||
if ( user && user.name )
|
||||
|
|
|
@ -115,6 +115,8 @@ FFZ.prototype.setup_room = function() {
|
|||
FFZ.prototype._modify_rview = function(view) {
|
||||
var f = this;
|
||||
view.reopen({
|
||||
alternate: false,
|
||||
|
||||
didInsertElement: function() {
|
||||
this._super();
|
||||
|
||||
|
@ -134,6 +136,10 @@ FFZ.prototype._modify_rview = function(view) {
|
|||
this._super();
|
||||
},
|
||||
|
||||
ffzUpdateAlternate: function() {
|
||||
this.get('element').classList.toggle('ffz-alternate', this.get('ffzAlternate'));
|
||||
}.observes("ffzAlternate"),
|
||||
|
||||
ffzInit: function() {
|
||||
f._roomv = this;
|
||||
|
||||
|
@ -1182,10 +1188,10 @@ FFZ.prototype._modify_room = function(room) {
|
|||
return;
|
||||
|
||||
var is_whisper = msg.style === 'whisper';
|
||||
if ( f.settings.group_tabs && f.settings.whisper_room ) {
|
||||
if ( ( is_whisper && ! this.ffz_whisper_room ) || ( ! is_whisper && this.ffz_whisper_room ) )
|
||||
return;
|
||||
}
|
||||
|
||||
// Ignore whispers if conversations are enabled.
|
||||
if ( is_whisper && App.__container__.lookup('route:application').controller.get('isConversationsEnabled') )
|
||||
return;
|
||||
|
||||
if ( ! is_whisper )
|
||||
msg.room = this.get('id');
|
||||
|
|
|
@ -262,7 +262,7 @@ FFZ.prototype._emote_tooltip = function(emote) {
|
|||
title = set && set.title || "Global",
|
||||
source = set && set.source || "FFZ";
|
||||
|
||||
emote._tooltip = "Emoticon: " + (emote.hidden ? "???" : emote.name) + "\n" + source + " " + title + (owner ? "\nBy: " + owner.display_name : "");
|
||||
emote._tooltip = "Emoticon: " + (emote.hidden ? "???" : emote.name) + "<br>" + source + " " + title + (owner ? "<br>By: " + owner.display_name : "");
|
||||
return emote._tooltip;
|
||||
}
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ FFZ.get = function() { return FFZ.instance; }
|
|||
|
||||
// Version
|
||||
var VER = FFZ.version_info = {
|
||||
major: 3, minor: 5, revision: 57,
|
||||
major: 3, minor: 5, revision: 62,
|
||||
toString: function() {
|
||||
return [VER.major, VER.minor, VER.revision].join(".") + (VER.extra || "");
|
||||
}
|
||||
|
@ -123,11 +123,13 @@ require('./ember/room');
|
|||
require('./ember/layout');
|
||||
require('./ember/line');
|
||||
require('./ember/chatview');
|
||||
require('./ember/conversations');
|
||||
require('./ember/viewers');
|
||||
require('./ember/moderation-card');
|
||||
require('./ember/chat-input');
|
||||
//require('./ember/teams');
|
||||
require('./ember/directory');
|
||||
require('./ember/following');
|
||||
|
||||
require('./debug');
|
||||
|
||||
|
@ -335,10 +337,12 @@ FFZ.prototype.init_ember = function(delay) {
|
|||
this.setup_line();
|
||||
this.setup_layout();
|
||||
this.setup_chatview();
|
||||
this.setup_conversations();
|
||||
this.setup_viewers();
|
||||
this.setup_mod_card();
|
||||
this.setup_chat_input();
|
||||
this.setup_directory();
|
||||
this.setup_profile_following();
|
||||
|
||||
//this.setup_teams();
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ var FFZ = window.FrankerFaceZ,
|
|||
set_type = null;
|
||||
}
|
||||
|
||||
return "Emoticon: " + data.code + "\n" + (set_type ? set_type + ": " : "") + set + (owner ? "\nBy: " + owner.display_name : "");
|
||||
return "Emoticon: " + data.code + "<br>" + (set_type ? set_type + ": " : "") + set + (owner ? "<br>By: " + owner.display_name : "");
|
||||
},
|
||||
|
||||
build_tooltip = function(id) {
|
||||
|
@ -603,7 +603,7 @@ FFZ.prototype.render_tokens = function(tokens, render_links) {
|
|||
var emote_set = f.emote_sets && f.emote_sets[token.ffzEmoteSet],
|
||||
emote = emote_set && emote_set.emoticons && emote_set.emoticons[token.ffzEmote];
|
||||
|
||||
tooltip = emote ? utils.sanitize(f._emote_tooltip(emote)) : token.altText;
|
||||
tooltip = emote ? f._emote_tooltip(emote) : token.altText;
|
||||
srcset = emote ? emote.srcSet : token.srcSet;
|
||||
extra = (emote ? ' data-ffz-emote="' + emote.id + '"' : '') + (emote_set ? ' data-ffz-set="' + emote_set.id + '"' : '');
|
||||
|
||||
|
@ -652,7 +652,7 @@ FFZ.prototype.render_tokens = function(tokens, render_links) {
|
|||
srcset = build_srcset(id);
|
||||
}
|
||||
|
||||
return '<img class="emoticon tooltip' + (cls||"") + '"' + (extra||"") + ' src="' + utils.quote_attr(src) + '" ' + (srcset ? 'srcset="' + utils.quote_attr(srcset) + '" ' : '') + 'alt="' + utils.quote_attr(token.altText) + '" title="' + utils.quote_attr(tooltip) + '">';
|
||||
return '<img class="emoticon html-tooltip' + (cls||"") + '"' + (extra||"") + ' src="' + utils.quote_attr(src) + '" ' + (srcset ? 'srcset="' + utils.quote_attr(srcset) + '" ' : '') + 'alt="' + utils.quote_attr(token.altText) + '" title="' + utils.quote_attr(tooltip) + '">';
|
||||
}
|
||||
|
||||
if ( token.isLink ) {
|
||||
|
|
|
@ -224,6 +224,9 @@ FFZ.prototype.build_ui_popup = function(view) {
|
|||
|
||||
container.classList.toggle('dark', dark);
|
||||
|
||||
// Stuff
|
||||
jQuery(inner).find('.html-tooltip').tipsy({live: true, html: true, gravity: jQuery.fn.tipsy.autoNS});
|
||||
|
||||
|
||||
// Menu Container
|
||||
var sub_container = document.createElement('div');
|
||||
|
@ -599,7 +602,7 @@ FFZ.prototype._emotes_for_sets = function(parent, view, sets, header, image, sub
|
|||
|
||||
c++;
|
||||
var s = document.createElement('span');
|
||||
s.className = 'emoticon tooltip';
|
||||
s.className = 'emoticon html-tooltip';
|
||||
s.style.backgroundImage = 'url("' + emote.urls[1] + '")';
|
||||
|
||||
if ( srcset ) {
|
||||
|
|
|
@ -166,7 +166,7 @@ FFZ.menu_pages.myemotes = {
|
|||
if ( (settings === 1 && ! emoji.tw) || (settings === 2 && ! emoji.noto) )
|
||||
continue;
|
||||
|
||||
em.className = 'emoticon tooltip';
|
||||
em.className = 'emoticon html-tooltip';
|
||||
em.title = 'Emoji: ' + emoji.raw + '\nName: ' + emoji.name + (emoji.short_name ? '\nShort Name: :' + emoji.short_name + ':' : '');
|
||||
em.addEventListener('click', this._add_emote.bind(this, view, emoji.raw));
|
||||
|
||||
|
@ -239,7 +239,7 @@ FFZ.menu_pages.myemotes = {
|
|||
em = document.createElement('span'),
|
||||
img_set = 'image-set(url("' + TWITCH_BASE + emote.id + '/1.0") 1x, url("' + TWITCH_BASE + emote.id + '/2.0") 2x, url("' + TWITCH_BASE + emote.id + '/3.0") 4x)';
|
||||
|
||||
em.className = 'emoticon tooltip';
|
||||
em.className = 'emoticon html-tooltip';
|
||||
|
||||
if ( this.settings.replace_bad_emotes && constants.EMOTE_REPLACEMENTS[emote.id] ) {
|
||||
em.style.backgroundImage = 'url("' + constants.EMOTE_REPLACEMENT_BASE + constants.EMOTE_REPLACEMENTS[emote.id] + '")';
|
||||
|
@ -316,7 +316,7 @@ FFZ.menu_pages.myemotes = {
|
|||
|
||||
img_set += ')';
|
||||
|
||||
em.className = 'emoticon tooltip';
|
||||
em.className = 'emoticon html-tooltip';
|
||||
em.style.backgroundImage = 'url("' + emote.urls[1] + '")';
|
||||
em.style.backgroundImage = '-webkit-' + img_set;
|
||||
em.style.backgroundImage = '-moz-' + img_set;
|
||||
|
|
|
@ -16,7 +16,7 @@ FFZ.prototype.setup_css = function() {
|
|||
/*var s = this._main_style = document.createElement('style');
|
||||
|
||||
s.textContent = styles.style;
|
||||
s.id = "ffz-ui-css";
|
||||
s.id = "ffz-main-css";
|
||||
|
||||
document.head.appendChild(s);*/
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue