mirror of
https://github.com/FrankerFaceZ/FrankerFaceZ.git
synced 2025-09-15 17:46:55 +00:00
4.15.0
* Added: New appearance type for chat actions that includes both an icon and text. * Changed: The emote menu now displays channel avatars rather than sub badges. * Changed: Emotes unlocked via points or other methods will appear as unlocked on the Channel page of the emote menu, even if you aren't subscribed. * Changed: Display the number of months someone has been subscribed on the Founder badge. (Closes #694) * Removed: Setting to not automatically redirect to Squad Stream pages, as Twitch is no longer doing that. * Fixed: The emote menu now correctly groups emotes with no specific source channel. * Fixed: The emote menu should no longer display sets as `Set #Number`. * Fixed: The emote data module constantly trying to load data for a set that does not exist rather than storing that it does not exist. * Fixed: Cache emote set data from the emote menu for use with tool-tips. * Fixed: Clicking buttons in the FFZ Control Center's header now prevents dragging from starting. * Fixed: Remove several Fine instances that were not resolving to anything useful. * Fixed: Alignment of in-line mod actions. * Behind the Scenes: Started fresh work on custom viewer cards.
This commit is contained in:
parent
5a235f9847
commit
8cfbc95821
39 changed files with 610 additions and 303 deletions
42
src/modules/chat/actions/components/edit-combo.vue
Normal file
42
src/modules/chat/actions/components/edit-combo.vue
Normal file
|
@ -0,0 +1,42 @@
|
|||
<template lang="html">
|
||||
<div>
|
||||
<div class="tw-flex tw-align-items-start">
|
||||
<label class="tw-mg-y-05">
|
||||
{{ t('setting.actions.icon', 'Icon') }}
|
||||
</label>
|
||||
|
||||
<icon-picker
|
||||
:value="value.icon"
|
||||
class="tw-full-width"
|
||||
@input="change"
|
||||
/>
|
||||
</div>
|
||||
<div class="tw-flex tw-align-items-center">
|
||||
<label for="edit_text">
|
||||
{{ t('setting.actions.text', 'Label') }}
|
||||
</label>
|
||||
|
||||
<input
|
||||
id="edit_text"
|
||||
v-model="value.text"
|
||||
class="tw-border-radius-medium tw-font-size-6 tw-full-width tw-input tw-pd-x-1 tw-pd-y-05 tw-mg-y-05"
|
||||
@input="$emit('input', value)"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
props: ['value'],
|
||||
|
||||
methods: {
|
||||
change(val) {
|
||||
this.value.icon = val;
|
||||
this.$emit('input', this.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
3
src/modules/chat/actions/components/preview-combo.vue
Normal file
3
src/modules/chat/actions/components/preview-combo.vue
Normal file
|
@ -0,0 +1,3 @@
|
|||
<template functional>
|
||||
<span :style="{color:props.color}" :class="props.data.icon">{{ props.data.text }}</span>
|
||||
</template>
|
|
@ -1,5 +1,3 @@
|
|||
<template functional>
|
||||
<span :style="{color: props.color}">
|
||||
{{ props.data.text }}
|
||||
</span>
|
||||
<span :style="{color: props.color}">{{ props.data.text }}</span>
|
||||
</template>
|
|
@ -177,7 +177,7 @@ export default class Actions extends Module {
|
|||
|
||||
default: [
|
||||
{v: {action: 'friend'}},
|
||||
{v: {action: 'whisper', appearance: {type: 'text', text: 'Whisper', button: true}}},
|
||||
{v: {action: 'whisper', appearance: {type: 'combo', icon: '', text: 'Whisper', button: true}}},
|
||||
{v: {type: 'space'}},
|
||||
{v: {action: 'card_menu'}},
|
||||
{v: {type: 'new-line'}},
|
||||
|
|
|
@ -48,6 +48,34 @@ export const icon = {
|
|||
}
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// Text + Icon
|
||||
// ============================================================================
|
||||
|
||||
export const combo = {
|
||||
title: 'Text and Icon',
|
||||
title_i18n: 'setting.actions.appearance.combo',
|
||||
|
||||
colored: true,
|
||||
editor: () => import(/* webpackChunkName: 'main-menu' */ './components/edit-combo.vue'),
|
||||
|
||||
load(data) {
|
||||
if ( data.icon && data.icon.startsWith('ffz-fa') )
|
||||
loadFontAwesome();
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
component: () => import(/* webpackChunkName: 'main-menu' */ './components/preview-combo.vue'),
|
||||
|
||||
render(data, createElement, color) {
|
||||
return (<span style={{color}} class={data.icon}>
|
||||
{data.text}
|
||||
</span>)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// Image
|
||||
// ============================================================================
|
||||
|
|
|
@ -288,6 +288,11 @@ export default class Badges extends Module {
|
|||
title,
|
||||
count: d.data
|
||||
});
|
||||
} else if ( d.badge === 'founder' ) {
|
||||
title = this.i18n.t('badges.founder.months', '{title}\n(Subscribed for {count,number} Month{count,en_plural})', {
|
||||
title,
|
||||
count: d.data
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -371,7 +376,7 @@ export default class Badges extends Module {
|
|||
else
|
||||
slot = last_slot++;
|
||||
|
||||
const data = dynamic_data[badge_id],
|
||||
const data = dynamic_data[badge_id] || (badge_id === 'founder' && dynamic_data['subscriber']),
|
||||
urls = badge_id === 'moderator' && custom_mod && room && room.data && room.data.mod_urls,
|
||||
badges = [];
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ import GET_EMOTE from './emote_info.gql';
|
|||
|
||||
const MOD_KEY = IS_OSX ? 'metaKey' : 'ctrlKey';
|
||||
|
||||
const EXTRA_INVENTORY = [33563];
|
||||
//const EXTRA_INVENTORY = [33563];
|
||||
|
||||
const MODIFIERS = {
|
||||
59847: {
|
||||
|
@ -64,7 +64,7 @@ export default class Emotes extends Module {
|
|||
this.inject('settings');
|
||||
this.inject('experiments');
|
||||
|
||||
this.twitch_inventory_sets = new Set(EXTRA_INVENTORY);
|
||||
this.twitch_inventory_sets = new Set; //(EXTRA_INVENTORY);
|
||||
this.__twitch_emote_to_set = new Map;
|
||||
this.__twitch_set_to_channel = new Map;
|
||||
|
||||
|
@ -127,7 +127,7 @@ export default class Emotes extends Module {
|
|||
|
||||
onEnable() {
|
||||
// Just in case there's a weird load order going on.
|
||||
this.on('site:enabled', this.loadTwitchInventory);
|
||||
// this.on('site:enabled', this.loadTwitchInventory);
|
||||
|
||||
this.style = new ManagedStyle('emotes');
|
||||
|
||||
|
@ -146,7 +146,7 @@ export default class Emotes extends Module {
|
|||
this.socket.on(':command:follow_sets', this.updateFollowSets, this);
|
||||
|
||||
this.loadGlobalSets();
|
||||
this.loadTwitchInventory();
|
||||
//this.loadTwitchInventory();
|
||||
}
|
||||
|
||||
|
||||
|
@ -788,6 +788,21 @@ export default class Emotes extends Module {
|
|||
}
|
||||
|
||||
|
||||
setTwitchEmoteSet(emote_id, set_id) {
|
||||
if ( isNaN(emote_id) || ! isFinite(emote_id) )
|
||||
return;
|
||||
|
||||
this.__twitch_emote_to_set.set(emote_id, set_id);
|
||||
}
|
||||
|
||||
setTwitchSetChannel(set_id, channel) {
|
||||
if ( isNaN(set_id) || ! isFinite(set_id) )
|
||||
return;
|
||||
|
||||
this.__twitch_set_to_channel.set(set_id, channel);
|
||||
}
|
||||
|
||||
|
||||
getTwitchEmoteSet(emote_id, callback) {
|
||||
const tes = this.__twitch_emote_to_set;
|
||||
|
||||
|
@ -869,6 +884,9 @@ export default class Emotes extends Module {
|
|||
return data;
|
||||
|
||||
} catch(err) {
|
||||
if ( err === 'No known Twitch emote set with that ID.' )
|
||||
return null;
|
||||
|
||||
tes.delete(set_id);
|
||||
}
|
||||
}
|
||||
|
@ -896,6 +914,15 @@ export default class Emotes extends Module {
|
|||
if ( callback )
|
||||
callback(data);
|
||||
|
||||
}).catch(() => tes.delete(set_id));
|
||||
}).catch(err => {
|
||||
if ( err === 'No known Twitch emote set with that ID.' ) {
|
||||
if ( callback )
|
||||
callback(null);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
tes.delete(set_id)
|
||||
});
|
||||
}
|
||||
}
|
|
@ -59,7 +59,7 @@ export default class Logviewer extends Module {
|
|||
if ( ! card.channel || ! card.user )
|
||||
return;
|
||||
|
||||
card.lv_topic = `logs-${card.channel.login}-${card.channel.user}`;
|
||||
card.lv_topic = `logs-${card.channel.login}-${card.user.login}`;
|
||||
this.lv_socket.subscribe(card, card.lv_topic);
|
||||
}
|
||||
|
||||
|
|
|
@ -219,7 +219,10 @@ export default {
|
|||
this.displace = displace(this.$el, {
|
||||
handle: this.$el.querySelector('header'),
|
||||
highlightInputs: true,
|
||||
constrain: true
|
||||
constrain: true,
|
||||
ignoreFn(event) {
|
||||
return event.target.closest('button') != null
|
||||
}
|
||||
});
|
||||
})
|
||||
},
|
||||
|
|
|
@ -1,96 +1,115 @@
|
|||
<template>
|
||||
<div
|
||||
:style="{zIndex: z}"
|
||||
class="ffz-mod-card tw-elevation-3 tw-c-background-alt tw-c-text-base tw-border tw-flex tw-flex-nowrap tw-flex-column"
|
||||
class="ffz-viewer-card tw-border-radius-medium tw-c-background-base tw-c-text-base tw-elevation-2 tw-flex tw-flex-column viewer-card"
|
||||
tabindex="0"
|
||||
@focusin="onFocus"
|
||||
@keyup.esc="close"
|
||||
>
|
||||
<header
|
||||
<div
|
||||
:style="loaded && `background-image: url('${user.bannerImageURL}');`"
|
||||
class="tw-full-width tw-align-items-center tw-flex tw-flex-nowrap tw-relative"
|
||||
class="ffz-viewer-card__header tw-c-background-accent-alt tw-flex-grow-0 tw-flex-shrink-0 viewer-card__background tw-relative"
|
||||
>
|
||||
<div class="tw-full-width tw-align-items-center tw-flex tw-flex-nowrap tw-pd-1 ffz--background-dimmer">
|
||||
<figure class="tw-avatar tw-avatar--size-50">
|
||||
<div v-if="loaded" class="tw-overflow-hidden ">
|
||||
<img
|
||||
:src="user.profileImageURL"
|
||||
class="tw-image"
|
||||
>
|
||||
<div class="tw-flex tw-flex-column tw-full-height tw-full-width viewer-card__overlay">
|
||||
<div class="tw-align-center tw-align-items-start tw-c-background-overlay tw-c-text-overlay tw-flex tw-flex-grow-1 tw-flex-row tw-full-width tw-justify-content-start tw-pd-05 tw-relative viewer-card__banner">
|
||||
<div class="tw-mg-l-05 tw-mg-y-05 tw-inline-flex viewer-card-drag-cancel">
|
||||
<figure class="tw-avatar tw-avatar--size-50">
|
||||
<img
|
||||
v-if="loaded"
|
||||
:src="user.profileImageURL"
|
||||
:alt="displayName"
|
||||
class="tw-block tw-border-radius-rounded tw-image tw-image-avatar"
|
||||
>
|
||||
</figure>
|
||||
</div>
|
||||
</figure>
|
||||
<div class="tw-ellipsis tw-inline-block">
|
||||
<div class="tw-align-items-center tw-mg-l-1 ffz--info-lines">
|
||||
<h4>
|
||||
<a :href="`/${login}`" class="tw-link tw-link--hover-underline-none tw-link--inherit" target="_blank">
|
||||
{{ displayName }}
|
||||
</a>
|
||||
</h4>
|
||||
<h5
|
||||
v-if="displayName && displayName.toLowerCase() !== login"
|
||||
>
|
||||
<a :href="`/${login}`" class="tw-link tw-link--hover-underline-none tw-link--inherit" target="_blank">
|
||||
{{ login }}
|
||||
</a>
|
||||
</h5>
|
||||
<div class="tw-align-left tw-flex-grow-1 tw-ellipsis tw-mg-x-1 tw-mg-y-05 viewer-card__display-name">
|
||||
<div class="tw-inline-flex">
|
||||
<h4 class="viewer-card-drag-cancel">
|
||||
<a
|
||||
:href="`/${login}`"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="tw-interactive tw-link tw-link--hover-color-inherit tw-link--inherit"
|
||||
>
|
||||
{{ displayName }}
|
||||
</a>
|
||||
</h4>
|
||||
</div>
|
||||
<div v-if="loaded" class="tw-pd-t-05">
|
||||
<span
|
||||
<!--span
|
||||
:data-title="t('viewer-card.views', 'Views')"
|
||||
class="ffz-tooltip tw-mg-r-05 ffz-i-views"
|
||||
class="ffz-tooltip tw-mg-r-05 ffz-i-views viewer-card-drag-cancel"
|
||||
>
|
||||
{{ t(null, '{views,number}', {views: user.profileViewCount}) }}
|
||||
</span>
|
||||
<span
|
||||
{{ t('viewer-card.views.number', '{views,number}', {views: user.profileViewCount}) }}
|
||||
</span-->
|
||||
<!--span
|
||||
:data-title="t('viewer-card.followers', 'Followers')"
|
||||
class="ffz-tooltip tw-mg-r-05 ffz-i-heart"
|
||||
class="ffz-tooltip tw-mg-r-05 ffz-i-heart viewer-card-drag-cancel"
|
||||
>
|
||||
{{ t(null, '{followers,number}', {followers: user.followers.totalCount}) }}
|
||||
</span>
|
||||
{{ t('viewer-card.followers.number', '{followers,number}', {followers: user.followers.totalCount}) }}
|
||||
</span-->
|
||||
<span
|
||||
v-if="userAge"
|
||||
:data-title="t('viewer-card.age-tip', 'Member Since: {age,datetime}', {age: userAge})"
|
||||
class="ffz-tooltip ffz-i-clock"
|
||||
:data-title="t('viewer-card.age-tip', 'Account Created at: {created,datetime}', {created: userAge})"
|
||||
class="ffz-tooltip ffz-i-cake viewer-card-drag-cancel"
|
||||
>
|
||||
{{ t('viewer-card.age', '{age,humantime}', {age: userAge}) }}
|
||||
{{ t('viewer-card.age', '{created,humantime}', {created: userAge}) }}
|
||||
</span>
|
||||
<span
|
||||
v-if="followAge"
|
||||
:data-title="t('viewer-card.follow-tip', 'Followed at: {followed,datetime}', {followed: followAge})"
|
||||
class="ffz-tooltip ffz-i-heart viewer-card-drag-cancel"
|
||||
>
|
||||
{{ t('viewer-card.follow', '{followed,humantime}', {followed: followAge}) }}
|
||||
</span>
|
||||
<span
|
||||
v-if="subscription"
|
||||
:data-title="t('viewer-card.months-tip', 'Subscribed for {months,number} month{months,en_plural}', {months: subscription.months})"
|
||||
class="ffz-tooltip ffz-i-star viewer-card-drag-cancel"
|
||||
>
|
||||
{{ t('viewer-card.months', '{months,number} month{months,en_plural}', {months: subscription.months}) }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tw-flex tw-flex-column">
|
||||
<button
|
||||
:data-title="t('viewer-card.close', 'Close')"
|
||||
:aria-label="t('viewer-card.close', 'Close')"
|
||||
class="viewer-card-drag-cancel tw-align-items-center tw-align-middle tw-border-radius-medium tw-button-icon tw-button-icon--overlay tw-core-button tw-core-button--overlay tw-inline-flex tw-interactive tw-justify-content-center tw-overflow-hidden tw-relative ffz-tooltip"
|
||||
@click="close"
|
||||
>
|
||||
<span class="tw-button-icon__icon">
|
||||
<figure class="ffz-i-cancel" />
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
v-if="! pinned"
|
||||
:data-title="t('viewer-card.pin', 'Pin')"
|
||||
:aria-label="t('viewer-card.pin', 'Pin')"
|
||||
class="viewer-card-drag-cancel tw-align-items-center tw-align-middle tw-border-radius-medium tw-button-icon tw-button-icon--overlay tw-core-button tw-core-button--overlay tw-inline-flex tw-interactive tw-justify-content-center tw-overflow-hidden tw-relative ffz-tooltip"
|
||||
@click="pin"
|
||||
>
|
||||
<span class="tw-button-icon__icon">
|
||||
<figure class="ffz-i-pin" />
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tw-flex-grow-1 tw-pd-x-2" />
|
||||
<button
|
||||
:data-title="t('viewer-card.close', 'Close')"
|
||||
class="ffz-tooltip tw-button-icon tw-absolute tw-right-0 tw-top-0 tw-mg-t-05 tw-mg-r-05"
|
||||
@click="close"
|
||||
>
|
||||
<span class="tw-button-icon__icon">
|
||||
<figure class="ffz-i-cancel" />
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
v-show="! pinned"
|
||||
:data-title="t('viewer-card.pin', 'Pin')"
|
||||
class="ffz-tooltip tw-button-icon tw-absolute tw-right-0 tw-bottom-0 tw-mg-b-05 tw-mg-r-05"
|
||||
@click="pin"
|
||||
>
|
||||
<span class="tw-button-icon__icon">
|
||||
<figure class="ffz-i-pin" />
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
</div>
|
||||
<error-tab v-if="errored" />
|
||||
<template v-else-if="loaded">
|
||||
<section class="tw-c-background-base">
|
||||
<div class="mod-cards__tabs-container tw-border-t">
|
||||
<div class="viewer-card__tabs-container tw-border-t">
|
||||
<div
|
||||
v-for="(data, key) in tabs"
|
||||
v-for="(d, key) in tabs"
|
||||
:id="`viewer-card__${key}`"
|
||||
:key="key"
|
||||
:id="`mod-cards__${key}`"
|
||||
:class="{active: active_tab === key}"
|
||||
class="mod-cards__tab tw-pd-x-1"
|
||||
class="viewer-card__tab tw-pd-x-1"
|
||||
@click="active_tab = key"
|
||||
>
|
||||
<span>{{ data.label }}</span>
|
||||
<span>{{ d.label }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
@ -115,6 +134,8 @@
|
|||
import LoadingTab from './components/loading-tab.vue';
|
||||
import ErrorTab from './components/error-tab.vue';
|
||||
|
||||
import {deep_copy} from 'utilities/object';
|
||||
|
||||
import displace from 'displacejs';
|
||||
|
||||
export default {
|
||||
|
@ -123,7 +144,7 @@ export default {
|
|||
'loading-tab': LoadingTab
|
||||
},
|
||||
|
||||
props: ['tabs', 'room', 'raw_user', 'pos_x', 'pos_y', 'data', 'getZ', 'getFFZ'],
|
||||
props: ['tabs', 'room', 'raw_user', 'pos_x', 'pos_y', 'data', 'ban_info', 'getZ', 'getFFZ'],
|
||||
|
||||
data() {
|
||||
return {
|
||||
|
@ -133,10 +154,12 @@ export default {
|
|||
loaded: false,
|
||||
errored: false,
|
||||
pinned: false,
|
||||
twitch_banned: false,
|
||||
|
||||
user: null,
|
||||
channel: null,
|
||||
self: null
|
||||
self: null,
|
||||
ban: null
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -155,6 +178,13 @@ export default {
|
|||
return this.raw_user.displayName || this.raw_user.login;
|
||||
},
|
||||
|
||||
subscription() {
|
||||
if ( this.loaded )
|
||||
return this.user.relationship?.cumulativeTenure;
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
userAge() {
|
||||
if ( this.loaded )
|
||||
return new Date(this.user.createdAt);
|
||||
|
@ -162,6 +192,11 @@ export default {
|
|||
return null
|
||||
},
|
||||
|
||||
followAge() {
|
||||
const age = this.loaded && this.user?.relationship?.followedAt;
|
||||
return age ? new Date(age) : null;
|
||||
},
|
||||
|
||||
current_tab() {
|
||||
return this.tabs[this.active_tab];
|
||||
}
|
||||
|
@ -171,9 +206,10 @@ export default {
|
|||
this.$emit('emit', ':open', this);
|
||||
|
||||
this.data.then(data => {
|
||||
this.user = data.data.targetUser;
|
||||
this.channel = data.data.channelUser;
|
||||
this.self = data.data.currentUser;
|
||||
this.twitch_banned = data?.data?.activeTargetUser?.id !== data?.data?.targetUser?.id;
|
||||
this.user = deep_copy(data?.data?.targetUser);
|
||||
this.channel = deep_copy(data?.data?.channelUser);
|
||||
this.self = deep_copy(data?.data?.currentUser);
|
||||
this.loaded = true;
|
||||
|
||||
this.$emit('emit', ':load', this);
|
||||
|
@ -182,6 +218,14 @@ export default {
|
|||
console.error(err); // eslint-disable-line no-console
|
||||
this.errored = true;
|
||||
});
|
||||
|
||||
this.ban_info.then(data => {
|
||||
this.ban = deep_copy(data?.data?.chatRoomBanStatus);
|
||||
|
||||
}).catch(err => {
|
||||
console.error(err);
|
||||
this.ban = null;
|
||||
});
|
||||
},
|
||||
|
||||
mounted() {
|
||||
|
@ -236,6 +280,14 @@ export default {
|
|||
this.pinned = true;
|
||||
this.$emit('pin');
|
||||
this.$emit('emit', ':pin', this);
|
||||
|
||||
this.cleanTips();
|
||||
},
|
||||
|
||||
cleanTips() {
|
||||
this.$nextTick(() => {
|
||||
this.getFFZ().emit('tooltips:cleanup');
|
||||
});
|
||||
},
|
||||
|
||||
close() {
|
||||
|
@ -245,9 +297,10 @@ export default {
|
|||
createDrag() {
|
||||
this.$nextTick(() => {
|
||||
this.displace = displace(this.$el, {
|
||||
handle: this.$el.querySelector('header'),
|
||||
handle: this.$el.querySelector('.ffz-viewer-card__header'),
|
||||
highlightInputs: true,
|
||||
constrain: true
|
||||
constrain: true,
|
||||
ignoreFn: e => e.target.closest('.viewer-card-drag-cancel') != null
|
||||
});
|
||||
})
|
||||
},
|
||||
|
|
12
src/modules/viewer_cards/get_ban_status.gql
Normal file
12
src/modules/viewer_cards/get_ban_status.gql
Normal file
|
@ -0,0 +1,12 @@
|
|||
query FFZ_BanStatus($channelID: ID!, $targetUserID: ID!) {
|
||||
chatRoomBanStatus(channelID: $channelID, userID: $targetUserID) {
|
||||
createdAt
|
||||
expiresAt
|
||||
isPermanent
|
||||
moderator {
|
||||
id
|
||||
login
|
||||
displayName
|
||||
}
|
||||
}
|
||||
}
|
79
src/modules/viewer_cards/get_card_viewer.gql
Normal file
79
src/modules/viewer_cards/get_card_viewer.gql
Normal file
|
@ -0,0 +1,79 @@
|
|||
query FFZ_ViewerCard($channelID: ID!, $targetID: ID, $targetLogin: String) {
|
||||
activeTargetUser: user(id: $targetID, login: $targetLogin) {
|
||||
id
|
||||
}
|
||||
targetUser: user(id: $targetID, login: $targetLogin, lookupType: ALL) {
|
||||
id
|
||||
login
|
||||
displayName
|
||||
bannerImageURL
|
||||
profileImageURL(width: 70)
|
||||
createdAt
|
||||
profileViewCount
|
||||
followers {
|
||||
totalCount
|
||||
}
|
||||
...friendButtonFragment
|
||||
relationship(targetUserID: $channelID) {
|
||||
cumulativeTenure: subscriptionTenure(tenureMethod: CUMULATIVE) {
|
||||
daysRemaining
|
||||
months
|
||||
}
|
||||
followedAt
|
||||
subscriptionBenefit {
|
||||
id
|
||||
tier
|
||||
purchasedWithPrime
|
||||
gift {
|
||||
isGift
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
channelUser: user(id: $channelID) {
|
||||
id
|
||||
login
|
||||
displayName
|
||||
subscriptionProducts {
|
||||
id
|
||||
name
|
||||
price
|
||||
url
|
||||
emoteSetID
|
||||
emotes {
|
||||
id
|
||||
}
|
||||
tier
|
||||
}
|
||||
modLogsRoleAccess(role: MODERATOR) {
|
||||
accessLevel
|
||||
}
|
||||
self {
|
||||
banStatus {
|
||||
isPermanent
|
||||
}
|
||||
isModerator
|
||||
}
|
||||
}
|
||||
currentUser {
|
||||
id
|
||||
login
|
||||
roles {
|
||||
isSiteAdmin
|
||||
isStaff
|
||||
isGlobalMod
|
||||
}
|
||||
blockedUsers {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fragment friendButtonFragment on User {
|
||||
id
|
||||
self {
|
||||
friendship {
|
||||
__typename
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
query FFZ_ViewerCard($targetLogin: String!, $channelID: ID!) {
|
||||
targetUser: user(login: $targetLogin) {
|
||||
id
|
||||
login
|
||||
displayName
|
||||
bannerImageURL
|
||||
profileImageURL(width: 70)
|
||||
createdAt
|
||||
profileViewCount
|
||||
followers {
|
||||
totalCount
|
||||
}
|
||||
...friendButtonFragment
|
||||
}
|
||||
channelUser: user(id: $channelID) {
|
||||
id
|
||||
login
|
||||
displayName
|
||||
subscriptionProducts {
|
||||
id
|
||||
price
|
||||
url
|
||||
emoteSetID
|
||||
emotes {
|
||||
id
|
||||
}
|
||||
}
|
||||
self {
|
||||
isModerator
|
||||
}
|
||||
}
|
||||
currentUser {
|
||||
id
|
||||
login
|
||||
roles {
|
||||
isSiteAdmin
|
||||
isStaff
|
||||
isGlobalMod
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fragment friendButtonFragment on User {
|
||||
id
|
||||
self {
|
||||
friendship {
|
||||
__typename
|
||||
}
|
||||
}
|
||||
}
|
|
@ -7,7 +7,8 @@
|
|||
import Module from 'utilities/module';
|
||||
import {createElement} from 'utilities/dom';
|
||||
|
||||
import GET_USER_INFO from './get_user_info.gql';
|
||||
import GET_CARD_VIEWER from './get_card_viewer.gql';
|
||||
import GET_BAN_STATUS from './get_ban_status.gql';
|
||||
|
||||
export default class ViewerCards extends Module {
|
||||
constructor(...args) {
|
||||
|
@ -16,6 +17,7 @@ export default class ViewerCards extends Module {
|
|||
this.inject('i18n');
|
||||
this.inject('settings');
|
||||
this.inject('site.apollo');
|
||||
this.inject('site.twitch_data');
|
||||
|
||||
this.tabs = {};
|
||||
|
||||
|
@ -71,7 +73,17 @@ export default class ViewerCards extends Module {
|
|||
}
|
||||
|
||||
|
||||
async openCard(room, user, event) {
|
||||
async openCard(room, user, msg, event) {
|
||||
if ( typeof room === 'number' )
|
||||
room = await this.twitch_data.getUser(room);
|
||||
else if ( typeof room === 'string' )
|
||||
room = await this.twitch_data.getUser(undefined, room);
|
||||
|
||||
if ( typeof user === 'number' )
|
||||
user = await this.twitch_data.getUser(user);
|
||||
else if ( typeof user === 'string' )
|
||||
user = await this.twitch_data.getUser(undefined, user);
|
||||
|
||||
if ( user.userLogin && ! user.login )
|
||||
user = {
|
||||
login: user.userLogin,
|
||||
|
@ -79,6 +91,18 @@ export default class ViewerCards extends Module {
|
|||
displayName: user.userDisplayName,
|
||||
};
|
||||
|
||||
if ( ! room || (! room.id && ! room.login) )
|
||||
return;
|
||||
|
||||
if ( ! user || (! user.id && ! user.login) )
|
||||
return;
|
||||
|
||||
if ( ! user.id || ! user.login )
|
||||
user = await this.twitch_data.getUser(user.id, user.login);
|
||||
|
||||
if ( ! room.id || ! room.login )
|
||||
room = await this.twitch_data.getUser(room.id, room.login);
|
||||
|
||||
const old_card = this.open_cards[user.login];
|
||||
if ( old_card ) {
|
||||
old_card.$el.style.zIndex = ++this.last_z;
|
||||
|
@ -100,13 +124,21 @@ export default class ViewerCards extends Module {
|
|||
|
||||
// We start this first...
|
||||
const user_info = this.apollo.client.query({
|
||||
query: GET_USER_INFO,
|
||||
query: GET_CARD_VIEWER,
|
||||
variables: {
|
||||
targetLogin: user.login,
|
||||
channelID: room.id
|
||||
}
|
||||
});
|
||||
|
||||
const ban_info = this.apollo.client.query({
|
||||
query: GET_BAN_STATUS,
|
||||
variables: {
|
||||
targetUserID: user.id,
|
||||
channelID: room.id
|
||||
}
|
||||
});
|
||||
|
||||
// But we only wait on loading Vue, since we can show a loading indicator.
|
||||
await this.loadVue();
|
||||
|
||||
|
@ -115,13 +147,14 @@ export default class ViewerCards extends Module {
|
|||
room,
|
||||
user,
|
||||
user_info,
|
||||
ban_info,
|
||||
pos_x,
|
||||
pos_y,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
buildCard(room, user, data, pos_x, pos_y) {
|
||||
buildCard(room, user, data, ban_info, pos_x, pos_y) {
|
||||
let child;
|
||||
const component = new this.vue.Vue({
|
||||
el: createElement('div'),
|
||||
|
@ -131,6 +164,7 @@ export default class ViewerCards extends Module {
|
|||
room,
|
||||
raw_user: user,
|
||||
data,
|
||||
ban_info,
|
||||
|
||||
getFFZ: () => this,
|
||||
getZ: () => ++this.last_z
|
||||
|
|
|
@ -41,14 +41,14 @@ export default class Channel extends Module {
|
|||
}
|
||||
});
|
||||
|
||||
this.settings.add('channel.squads.no-autojoin', {
|
||||
/*this.settings.add('channel.squads.no-autojoin', {
|
||||
default: false,
|
||||
ui: {
|
||||
path: 'Channel > Behavior >> Squads',
|
||||
title: 'Do not automatically redirect to Squad Streams.',
|
||||
component: 'setting-check-box'
|
||||
}
|
||||
});
|
||||
});*/
|
||||
|
||||
|
||||
this.ChannelPage = this.fine.define(
|
||||
|
@ -63,11 +63,11 @@ export default class Channel extends Module {
|
|||
Twilight.CHAT_ROUTES
|
||||
);
|
||||
|
||||
this.SquadController = this.fine.define(
|
||||
/*this.SquadController = this.fine.define(
|
||||
'squad-controller',
|
||||
n => n.onSquadPage && n.isValidSquad && n.handleLeaveSquad,
|
||||
Twilight.CHAT_ROUTES
|
||||
);
|
||||
);*/
|
||||
}
|
||||
|
||||
|
||||
|
@ -76,8 +76,8 @@ export default class Channel extends Module {
|
|||
this.RaidController.on('mount', this.wrapRaidController, this);
|
||||
this.RaidController.on('update', this.noAutoRaids, this);
|
||||
|
||||
this.SquadController.on('mount', this.noAutoSquads, this);
|
||||
this.SquadController.on('update', this.noAutoSquads, this);
|
||||
//this.SquadController.on('mount', this.noAutoSquads, this);
|
||||
//this.SquadController.on('update', this.noAutoSquads, this);
|
||||
|
||||
this.RaidController.ready((cls, instances) => {
|
||||
for(const inst of instances)
|
||||
|
|
|
@ -51,13 +51,6 @@ export default class ChannelBar extends Module {
|
|||
n => n.getTitle && n.getGame && n.renderGame,
|
||||
['user']
|
||||
);
|
||||
|
||||
|
||||
this.HostBar = this.fine.define(
|
||||
'host-container',
|
||||
n => n.handleReportHosterClick,
|
||||
['user']
|
||||
)
|
||||
}
|
||||
|
||||
onEnable() {
|
||||
|
|
|
@ -13,6 +13,23 @@ import Module from 'utilities/module';
|
|||
|
||||
import SUB_STATUS from './sub_status.gql';
|
||||
|
||||
const GLOBAL_SETS = [
|
||||
0,
|
||||
33,
|
||||
42
|
||||
];
|
||||
|
||||
const POINTS_SETS = [
|
||||
300238151
|
||||
];
|
||||
|
||||
const PRIME_SETS = [
|
||||
457,
|
||||
793,
|
||||
19151,
|
||||
19194
|
||||
];
|
||||
|
||||
const TIERS = {
|
||||
1000: 'Tier 1',
|
||||
2000: 'Tier 2',
|
||||
|
@ -541,7 +558,7 @@ export default class EmoteMenu extends Module {
|
|||
|
||||
let image;
|
||||
if ( data.image )
|
||||
image = (<img class="ffz--menu-badge" src={data.image} srcSet={data.image_set} />);
|
||||
image = (<img class={`ffz--menu-badge${data.image_large ? ' ffz--menu-badge__large' : ''}`} src={data.image} srcSet={data.image_set} />);
|
||||
else
|
||||
image = (<figure class={`ffz--menu-badge ffz-i-${data.icon || 'zreknarf'}`} />);
|
||||
|
||||
|
@ -827,7 +844,7 @@ export default class EmoteMenu extends Module {
|
|||
this.handleObserve = this.handleObserve.bind(this);
|
||||
this.pickTone = this.pickTone.bind(this);
|
||||
this.clickTab = this.clickTab.bind(this);
|
||||
this.clickRefresh = this.clickRefresh.bind(this);
|
||||
//this.clickRefresh = this.clickRefresh.bind(this);
|
||||
this.handleFilterChange = this.handleFilterChange.bind(this);
|
||||
this.handleKeyDown = this.handleKeyDown.bind(this);
|
||||
}
|
||||
|
@ -946,7 +963,7 @@ export default class EmoteMenu extends Module {
|
|||
});
|
||||
}
|
||||
|
||||
clickRefresh(event) {
|
||||
/*clickRefresh(event) {
|
||||
const target = event.currentTarget,
|
||||
tt = target && target._ffz_tooltip$0;
|
||||
|
||||
|
@ -978,7 +995,7 @@ export default class EmoteMenu extends Module {
|
|||
Object.assign({}, this.state, {set_sets: sets, set_data: data, loading: false})
|
||||
)));
|
||||
});
|
||||
}
|
||||
}*/
|
||||
|
||||
handleFilterChange(event) {
|
||||
this.setState(this.filterState(event.target.value, this.state));
|
||||
|
@ -1006,18 +1023,10 @@ export default class EmoteMenu extends Module {
|
|||
|
||||
this.setState({loading: true}, () => {
|
||||
t.getData(sets, force).then(d => {
|
||||
const promises = [];
|
||||
|
||||
for(const set_id of sets)
|
||||
if ( ! has(d, set_id) )
|
||||
promises.push(t.emotes.awaitTwitchSetChannel(set_id))
|
||||
|
||||
Promise.all(promises).then(() => {
|
||||
this.setState(this.filterState(this.state.filter, this.buildState(
|
||||
this.props,
|
||||
Object.assign({}, this.state, {set_sets: sets, set_data: d, loading: false})
|
||||
)));
|
||||
});
|
||||
this.setState(this.filterState(this.state.filter, this.buildState(
|
||||
this.props,
|
||||
Object.assign({}, this.state, {set_sets: sets, set_data: d, loading: false})
|
||||
)));
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -1188,7 +1197,7 @@ export default class EmoteMenu extends Module {
|
|||
const sorter = this.getSorter(),
|
||||
sort_tiers = t.chat.context.get('chat.emote-menu.sort-tiers-last'),
|
||||
sort_emotes = (a,b) => {
|
||||
if ( a.inventory || b.inventory )
|
||||
if ( a.misc || b.misc )
|
||||
return sorter(a,b);
|
||||
|
||||
if ( ! a.locked && b.locked ) return -1;
|
||||
|
@ -1209,9 +1218,10 @@ export default class EmoteMenu extends Module {
|
|||
const emote_sets = props.emote_data && props.emote_data.emoteSets,
|
||||
emote_map = props.emote_data && props.emote_data.emoteMap,
|
||||
twitch_favorites = t.emotes.getFavorites('twitch'),
|
||||
twitch_seen_favorites = new Set,
|
||||
twitch_seen = new Set,
|
||||
|
||||
//twitch_seen_favorites = new Set,
|
||||
|
||||
inventory = t.emotes.twitch_inventory_sets || new Set,
|
||||
grouped_sets = {},
|
||||
set_ids = new Set;
|
||||
|
||||
|
@ -1221,58 +1231,58 @@ export default class EmoteMenu extends Module {
|
|||
continue;
|
||||
|
||||
const set_id = parseInt(emote_set.id, 10),
|
||||
is_inventory = inventory.has(set_id),
|
||||
set_data = data[set_id] || {},
|
||||
more_data = t.emotes.getTwitchSetChannel(set_id, null, false),
|
||||
image = set_data.image,
|
||||
image_set = set_data.image_set;
|
||||
owner = emote_set.owner,
|
||||
is_points = owner?.login === 'channel_points',
|
||||
chan = is_points ? null : owner,
|
||||
set_data = data[set_id];
|
||||
|
||||
console.log('set', set_id, owner);
|
||||
|
||||
if ( chan )
|
||||
t.emotes.setTwitchSetChannel(set_id, {
|
||||
s_id: set_id,
|
||||
c_id: chan.id,
|
||||
c_name: chan.login,
|
||||
c_title: chan.displayName
|
||||
});
|
||||
|
||||
set_ids.add(set_id);
|
||||
|
||||
let chan = set_data && set_data.user;
|
||||
if ( ! chan && more_data && more_data.c_id )
|
||||
chan = {
|
||||
id: more_data.c_id,
|
||||
login: more_data.c_name,
|
||||
display_name: more_data.c_name,
|
||||
bad: true
|
||||
};
|
||||
|
||||
let key = `twitch-set-${set_id}`,
|
||||
sort_key = 0,
|
||||
icon = 'twitch',
|
||||
title = chan && chan.display_name;
|
||||
title = chan && (chan.displayName || chan.login);
|
||||
|
||||
if ( title )
|
||||
key = `twitch-${chan.id}`;
|
||||
key = `twitch-${chan?.id}`;
|
||||
|
||||
else {
|
||||
if ( is_inventory ) {
|
||||
title = t.i18n.t('emote-menu.inventory', 'Inventory');
|
||||
key = 'twitch-inventory';
|
||||
icon = 'inventory';
|
||||
sort_key = 50;
|
||||
else if ( ! chan ) {
|
||||
if ( is_points || POINTS_SETS.includes(set_id) ) {
|
||||
title = t.i18n.t('emote-menu.points', 'Unlocked with Points');
|
||||
key = 'twitch-points';
|
||||
icon = 'channel-points';
|
||||
sort_key = 45;
|
||||
|
||||
} else if ( set_data && set_data.type === 'turbo' ) {
|
||||
title = t.i18n.t('emote-menu.prime', 'Prime');
|
||||
} else if ( GLOBAL_SETS.includes(set_id) ) {
|
||||
title = t.i18n.t('emote-menu.global', 'Global Emotes');
|
||||
key = 'twitch-global';
|
||||
sort_key = 100;
|
||||
|
||||
} else if ( PRIME_SETS.includes(set_id) ) {
|
||||
title = t.i18n.t('emote_menu.prime', 'Prime');
|
||||
key = 'twitch-prime';
|
||||
icon = 'crown';
|
||||
sort_key = 75;
|
||||
|
||||
} else if ( more_data ) {
|
||||
title = more_data.c_name;
|
||||
} else {
|
||||
title = t.i18n.t('emote-menu.misc', 'Miscellaneous');
|
||||
key = 'twitch-misc';
|
||||
icon = 'inventory';
|
||||
sort_key = 50;
|
||||
}
|
||||
|
||||
if ( title === '--global--' ) {
|
||||
title = t.i18n.t('emote-menu.global', 'Global Emotes');
|
||||
sort_key = 100;
|
||||
|
||||
} else if ( title === '--twitch-turbo--' || title === 'turbo' || title === '--turbo-faces--' || title === '--prime--' || title === '--prime-faces--' ) {
|
||||
title = t.i18n.t('emote-menu.prime', 'Prime');
|
||||
icon = 'crown';
|
||||
sort_key = 75;
|
||||
}
|
||||
} else
|
||||
title = t.i18n.t('emote-menu.unknown-set', 'Set #{set_id}', {set_id})
|
||||
}
|
||||
} else
|
||||
title = t.i18n.t('emote-menu.unknown-set', 'Set #{set_id}', {set_id})
|
||||
|
||||
let section, emotes;
|
||||
|
||||
|
@ -1280,32 +1290,33 @@ export default class EmoteMenu extends Module {
|
|||
section = grouped_sets[key];
|
||||
emotes = section.emotes;
|
||||
|
||||
if ( chan && ! chan.bad && section.bad ) {
|
||||
section.title = title;
|
||||
section.image = image;
|
||||
section.image_set = image_set;
|
||||
section.icon = icon;
|
||||
section.sort_key = sort_key;
|
||||
if ( set_data && section.bad ) {
|
||||
section.bad = false;
|
||||
section.renews = set_data.renews;
|
||||
section.ends = set_data.ends;
|
||||
section.prime = set_data.prime;
|
||||
section.gift = set_data.gift;
|
||||
}
|
||||
|
||||
} else {
|
||||
emotes = [];
|
||||
section = grouped_sets[key] = {
|
||||
sort_key,
|
||||
bad: chan ? chan.bad : true,
|
||||
key,
|
||||
image,
|
||||
image_set,
|
||||
image: chan?.profileImageURL,
|
||||
image_large: true,
|
||||
icon,
|
||||
title,
|
||||
source: t.i18n.t('emote-menu.twitch', 'Twitch'),
|
||||
emotes,
|
||||
renews: set_data.renews,
|
||||
ends: set_data.ends,
|
||||
prime: set_data.prime,
|
||||
gift: set_data.gift && set_data.gift.isGift
|
||||
renews: set_data?.renews,
|
||||
ends: set_data?.ends,
|
||||
prime: set_data?.prime,
|
||||
gift: set_data?.gift
|
||||
}
|
||||
|
||||
if ( ! set_data )
|
||||
section.bad = true;
|
||||
}
|
||||
|
||||
for(const emote of emote_set.emotes) {
|
||||
|
@ -1339,15 +1350,17 @@ export default class EmoteMenu extends Module {
|
|||
src,
|
||||
srcSet,
|
||||
overridden: overridden ? parseInt(mapped.id,10) : null,
|
||||
inventory: is_inventory,
|
||||
misc: ! chan,
|
||||
favorite: is_fav
|
||||
};
|
||||
|
||||
t.emotes.setTwitchEmoteSet(id, set_id);
|
||||
emotes.push(em);
|
||||
if ( is_fav && ! twitch_seen_favorites.has(id) ) {
|
||||
|
||||
if ( is_fav && ! twitch_seen.has(id) )
|
||||
favorites.push(em);
|
||||
twitch_seen_favorites.add(id);
|
||||
}
|
||||
|
||||
twitch_seen.add(id);
|
||||
}
|
||||
|
||||
if ( emotes.length ) {
|
||||
|
@ -1422,6 +1435,7 @@ export default class EmoteMenu extends Module {
|
|||
const id = parseInt(emote.id, 10),
|
||||
base = `${TWITCH_EMOTE_BASE}${id}`,
|
||||
name = KNOWN_CODES[emote.token] || emote.token,
|
||||
seen = twitch_seen.has(id),
|
||||
is_fav = twitch_favorites.includes(id);
|
||||
|
||||
const em = {
|
||||
|
@ -1429,7 +1443,7 @@ export default class EmoteMenu extends Module {
|
|||
id,
|
||||
set_id,
|
||||
name,
|
||||
locked,
|
||||
locked: locked && ! seen,
|
||||
src: `${base}/1.0`,
|
||||
srcSet: `${base}/1.0 1x, ${base}/2.0 2x`,
|
||||
favorite: is_fav
|
||||
|
@ -1437,10 +1451,10 @@ export default class EmoteMenu extends Module {
|
|||
|
||||
emotes.push(em);
|
||||
|
||||
if ( ! locked && is_fav && ! twitch_seen_favorites.has(id) ) {
|
||||
if ( ! locked && is_fav && ! seen )
|
||||
favorites.push(em);
|
||||
twitch_seen_favorites.add(id);
|
||||
}
|
||||
|
||||
twitch_seen.add(id);
|
||||
|
||||
if ( lock_set )
|
||||
lock_set.add(id);
|
||||
|
@ -1769,7 +1783,7 @@ export default class EmoteMenu extends Module {
|
|||
</div>
|
||||
</button>
|
||||
</div>}
|
||||
<div class="tw-flex-grow-1" />
|
||||
{/*<div class="tw-flex-grow-1" />
|
||||
{!loading && (<div class="emote-picker-tab-item tw-relative">
|
||||
<button
|
||||
class="ffz-tooltip tw-block tw-full-width tw-interactable tw-interactable--hover-enabled tw-interactable--inverted tw-interactive"
|
||||
|
@ -1781,7 +1795,7 @@ export default class EmoteMenu extends Module {
|
|||
<figure class="ffz-i-arrows-cw" />
|
||||
</div>
|
||||
</button>
|
||||
</div>)}
|
||||
</div>)*/}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1841,7 +1855,16 @@ export default class EmoteMenu extends Module {
|
|||
if ( ! set_id )
|
||||
continue;
|
||||
|
||||
const owner = product.owner || {},
|
||||
out[set_id] = {
|
||||
ends: maybe_date(node.endsAt),
|
||||
renews: maybe_date(node.renewsAt),
|
||||
prime: node.purchasedWithPrime,
|
||||
set_id: parseInt(set_id, 10),
|
||||
type: product.type,
|
||||
gift: node.gift?.isGift
|
||||
};
|
||||
|
||||
/*const owner = product.owner || {},
|
||||
badges = owner.broadcastBadges;
|
||||
|
||||
let image, image_set;
|
||||
|
@ -1852,7 +1875,7 @@ export default class EmoteMenu extends Module {
|
|||
|
||||
if ( image.endsWith('/1') ) {
|
||||
const base = image.slice(0, -2);
|
||||
image_set = `${base}/1 1x, ${base}/2 2x, ${base}/4 4x`;
|
||||
image_set = `${base}/1 1x, ${base}/2 2x, ${base}/3 4x`;
|
||||
}
|
||||
|
||||
break;
|
||||
|
@ -1872,7 +1895,7 @@ export default class EmoteMenu extends Module {
|
|||
login: owner.login,
|
||||
display_name: owner.displayName
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
this._data_sets = sets;
|
||||
|
|
|
@ -397,7 +397,7 @@ other {# messages were deleted by a moderator.}
|
|||
}
|
||||
|
||||
/*if ( event.ctrlKey )
|
||||
t.viewer_cards.openCard(r, target_user, event);
|
||||
t.viewer_cards.openCard(r, target_user, msg, event);
|
||||
else*/
|
||||
this.props.onUsernameClick(target_user.login, 'chat_message', msg.id, target.getBoundingClientRect().bottom);
|
||||
}
|
||||
|
|
|
@ -259,6 +259,15 @@ export default class Scroller extends Module {
|
|||
}
|
||||
|
||||
inst.scrollToBottom = function() {
|
||||
if ( inst._ffz_scroll_request )
|
||||
return;
|
||||
|
||||
inst._ffz_scroll_request = requestAnimationFrame(inst._scrollToBottom);
|
||||
}
|
||||
|
||||
inst._scrollToBottom = function() {
|
||||
inst._ffz_scroll_request = null;
|
||||
|
||||
// WIP: Trying to fix the scroll position changing so that we can
|
||||
// smooth scroll from the previous position.
|
||||
if ( inst.ffz_smooth_scroll && ! inst._ffz_one_fast_scroll && inst._ffz_snapshot ) {
|
||||
|
|
|
@ -17,21 +17,7 @@ query FFZ_EmoteMenu_SubStatus($first: Int, $after: Cursor, $criteria: Subscripti
|
|||
renewsAt
|
||||
product {
|
||||
id
|
||||
name
|
||||
displayName
|
||||
emoteSetID
|
||||
type
|
||||
owner {
|
||||
id
|
||||
login
|
||||
displayName
|
||||
broadcastBadges {
|
||||
id
|
||||
setID
|
||||
version
|
||||
imageURL(size: NORMAL)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,11 +13,11 @@ const STYLE_VALIDATOR = document.createElement('span');
|
|||
const CLASSES = {
|
||||
'top-discover': '.navigation-link[data-a-target="discover-link"]',
|
||||
'side-nav': '.side-nav',
|
||||
'side-rec-channels': '.side-nav .recommended-channels,.side-nav .ffz--popular-channels',
|
||||
'side-rec-channels': '.side-nav .recommended-channels',
|
||||
'side-rec-friends': '.side-nav .recommended-friends',
|
||||
'side-friends': '.side-nav .online-friends',
|
||||
'side-closed-friends': '.side-nav--collapsed .online-friends',
|
||||
'side-closed-rec-channels': '.side-nav--collapsed .recommended-channels,.side-nav--collapsed .ffz--popular-channels',
|
||||
'side-closed-rec-channels': '.side-nav--collapsed .recommended-channels',
|
||||
'side-offline-channels': '.side-nav-card__link[href*="/videos/"],.side-nav-card[href*="/videos/"]',
|
||||
'side-rerun-channels': '.side-nav .ffz--side-nav-card-rerun',
|
||||
|
||||
|
|
|
@ -25,15 +25,10 @@ export default class Layout extends Module {
|
|||
n => n.computeStyles && n.navigationLinkSize
|
||||
);
|
||||
|
||||
this.RightColumn = this.fine.define(
|
||||
/*this.RightColumn = this.fine.define(
|
||||
'tw-rightcolumn',
|
||||
n => n.hideOnBreakpoint && n.handleToggleVisibility
|
||||
);
|
||||
|
||||
this.PopularChannels = this.fine.define(
|
||||
'nav-popular',
|
||||
n => n.getPopularChannels && n.props && has(n.props, 'locale')
|
||||
);
|
||||
);*/
|
||||
|
||||
this.SideBarChannels = this.fine.define(
|
||||
'nav-cards',
|
||||
|
@ -176,14 +171,6 @@ export default class Layout extends Module {
|
|||
this.css_tweaks.setVariable('portrait-extra-width', `${this.settings.get('layout.portrait-extra-width')}rem`);
|
||||
this.css_tweaks.setVariable('portrait-extra-height', `${this.settings.get('layout.portrait-extra-height')}rem`);
|
||||
|
||||
this.PopularChannels.ready((cls, instances) => {
|
||||
for(const inst of instances)
|
||||
this.updatePopular(inst);
|
||||
});
|
||||
|
||||
this.PopularChannels.on('mount', this.updatePopular, this);
|
||||
this.PopularChannels.on('update', this.updatePopular, this);
|
||||
|
||||
this.SideBarChannels.ready((cls, instances) => {
|
||||
for(const inst of instances)
|
||||
this.updateCardClass(inst);
|
||||
|
@ -192,7 +179,7 @@ export default class Layout extends Module {
|
|||
this.SideBarChannels.on('mount', this.updateCardClass, this);
|
||||
this.SideBarChannels.on('update', this.updateCardClass, this);
|
||||
|
||||
const t = this;
|
||||
/*const t = this;
|
||||
this.RightColumn.ready((cls, instances) => {
|
||||
cls.prototype.ffzHideOnBreakpoint = function() {
|
||||
try {
|
||||
|
@ -235,7 +222,7 @@ export default class Layout extends Module {
|
|||
window.addEventListener('resize', inst.hideOnBreakpoint);
|
||||
inst.hideOnBreakpoint();
|
||||
}
|
||||
});
|
||||
});*/
|
||||
}
|
||||
|
||||
get is_minimal() {
|
||||
|
@ -258,12 +245,6 @@ export default class Layout extends Module {
|
|||
} catch(err) { /* no-op */ }
|
||||
}
|
||||
|
||||
updatePopular(inst) {
|
||||
const node = this.fine.getChildNode(inst);
|
||||
if ( node )
|
||||
node.classList.add('ffz--popular-channels');
|
||||
}
|
||||
|
||||
updatePortraitMode() {
|
||||
for(const inst of this.RightColumn.instances)
|
||||
inst.hideOnBreakpoint();
|
||||
|
|
|
@ -101,6 +101,14 @@
|
|||
margin: 0 0.05rem !important
|
||||
}
|
||||
|
||||
span:before {
|
||||
margin: 0 0.2rem 0 0 !important;
|
||||
}
|
||||
|
||||
span:empty:before {
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
span {
|
||||
display: inline-block;
|
||||
min-width: 1.6rem;
|
||||
|
@ -270,6 +278,15 @@
|
|||
}
|
||||
}
|
||||
|
||||
&:not(.reduced-padding) {
|
||||
.ffz--menu-badge__large {
|
||||
width: 2.8rem;
|
||||
height: 2.8rem;
|
||||
|
||||
margin: -.5rem .5rem -.5rem -.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
&.reduced-padding {
|
||||
.tw-pd-1 {
|
||||
padding: 0.5rem !important;
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
.ffz-mod-card {
|
||||
width: 340px;
|
||||
.ffz-viewer-card {
|
||||
width: 34rem;
|
||||
z-index: 9001;
|
||||
|
||||
> header {
|
||||
cursor: move;
|
||||
&:focus {
|
||||
outline: none;
|
||||
box-shadow: var(--shadow-button-focus);
|
||||
}
|
||||
|
||||
background-size: cover;
|
||||
background-position: top;
|
||||
> div:first-child {
|
||||
cursor: move;
|
||||
}
|
||||
|
||||
.ffz-tooltip {
|
||||
|
@ -15,18 +17,14 @@
|
|||
}
|
||||
}
|
||||
|
||||
.ffz--background-dimmer {
|
||||
background-color: rgba(0, 0, 0, 0.8);
|
||||
}
|
||||
|
||||
.ffz--info-lines > * {
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.mod-cards__tabs-container {
|
||||
.viewer-card__tabs-container {
|
||||
height: 3rem;
|
||||
|
||||
> .mod-cards__tab {
|
||||
> .viewer-card__tab {
|
||||
position: relative;
|
||||
top: -.1rem;
|
||||
cursor: pointer;
|
||||
|
@ -39,4 +37,9 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.viewer-card__background {
|
||||
background-position: top;
|
||||
background-size: cover
|
||||
}
|
|
@ -87,5 +87,8 @@ export default [
|
|||
"t-pip-inactive",
|
||||
"docs",
|
||||
"t-reset-clicked",
|
||||
"t-reset"
|
||||
"t-reset",
|
||||
"whispers",
|
||||
"cake",
|
||||
"channel-points"
|
||||
];
|
Loading…
Add table
Add a link
Reference in a new issue