mirror of
https://github.com/FrankerFaceZ/FrankerFaceZ.git
synced 2025-07-05 10:38:30 +00:00
4.2.6
* Fixed: Update Switchboard to allow FFZ to load normally on Twitch's latest update, which updated their version of React Router. * Fixed: Apply the setting to hide Recommended Channels from the sidebar to Popular Channels as well.
This commit is contained in:
parent
4e0539103a
commit
17abc10f7c
5 changed files with 189 additions and 8 deletions
|
@ -151,7 +151,7 @@ ${typeof x[1] === 'string' ? x[1] : JSON.stringify(x[1], null, 4)}`
|
||||||
FrankerFaceZ.Logger = Logger;
|
FrankerFaceZ.Logger = Logger;
|
||||||
|
|
||||||
const VER = FrankerFaceZ.version_info = {
|
const VER = FrankerFaceZ.version_info = {
|
||||||
major: 4, minor: 2, revision: 5,
|
major: 4, minor: 2, revision: 6,
|
||||||
commit: __git_commit__,
|
commit: __git_commit__,
|
||||||
build: __webpack_hash__,
|
build: __webpack_hash__,
|
||||||
toString: () =>
|
toString: () =>
|
||||||
|
|
|
@ -11,11 +11,11 @@ import {has} from 'utilities/object';
|
||||||
const CLASSES = {
|
const CLASSES = {
|
||||||
'top-discover': '.top-nav__nav-link[data-a-target="discover-link"]',
|
'top-discover': '.top-nav__nav-link[data-a-target="discover-link"]',
|
||||||
'side-nav': '.side-nav',
|
'side-nav': '.side-nav',
|
||||||
'side-rec-channels': '.side-nav .recommended-channels',
|
'side-rec-channels': '.side-nav .recommended-channels,.side-nav .ffz--popular-channels',
|
||||||
'side-rec-friends': '.side-nav .recommended-friends',
|
'side-rec-friends': '.side-nav .recommended-friends',
|
||||||
'side-friends': '.side-nav .online-friends',
|
'side-friends': '.side-nav .online-friends',
|
||||||
'side-closed-friends': '.side-nav--collapsed .online-friends',
|
'side-closed-friends': '.side-nav--collapsed .online-friends',
|
||||||
'side-closed-rec-channels': '.side-nav--collapsed .recommended-channels',
|
'side-closed-rec-channels': '.side-nav--collapsed .recommended-channels,.side-nav--collapsed .ffz--popular-channels',
|
||||||
'side-offline-channels': '.side-nav-card__link[href*="/videos/"]',
|
'side-offline-channels': '.side-nav-card__link[href*="/videos/"]',
|
||||||
|
|
||||||
'prime-offers': '.top-nav__prime',
|
'prime-offers': '.top-nav__prime',
|
||||||
|
@ -77,7 +77,7 @@ export default class CSSTweaks extends Module {
|
||||||
default: 1,
|
default: 1,
|
||||||
ui: {
|
ui: {
|
||||||
path: 'Appearance > Layout >> Side Navigation',
|
path: 'Appearance > Layout >> Side Navigation',
|
||||||
title: 'Display Recommended Channels',
|
title: 'Display Recommended / Popular Channels',
|
||||||
component: 'setting-select-box',
|
component: 'setting-select-box',
|
||||||
data: [
|
data: [
|
||||||
{value: 0, title: 'Never'},
|
{value: 0, title: 'Never'},
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
import Module from 'utilities/module';
|
import Module from 'utilities/module';
|
||||||
|
import {has} from 'utilities/object';
|
||||||
|
|
||||||
const PORTRAIT_ROUTES = ['user', 'video', 'user-video', 'user-clip', 'user-videos', 'user-clips', 'user-collections', 'user-events', 'user-followers', 'user-following'];
|
const PORTRAIT_ROUTES = ['user', 'video', 'user-video', 'user-clip', 'user-videos', 'user-clips', 'user-collections', 'user-events', 'user-followers', 'user-following'];
|
||||||
const MINIMAL_ROUTES = ['popout', 'embed-chat'];
|
const MINIMAL_ROUTES = ['popout', 'embed-chat'];
|
||||||
|
@ -24,6 +25,11 @@ export default class Layout extends Module {
|
||||||
n => n.hideOnBreakpoint && n.handleToggleVisibility
|
n => n.hideOnBreakpoint && n.handleToggleVisibility
|
||||||
);
|
);
|
||||||
|
|
||||||
|
this.PopularChannels = this.fine.define(
|
||||||
|
'nav-popular',
|
||||||
|
n => n.getPopularChannels && n.props && has(n.props, 'locale')
|
||||||
|
);
|
||||||
|
|
||||||
this.settings.add('layout.portrait', {
|
this.settings.add('layout.portrait', {
|
||||||
default: false,
|
default: false,
|
||||||
ui: {
|
ui: {
|
||||||
|
@ -146,6 +152,14 @@ 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-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.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);
|
||||||
|
|
||||||
const t = this;
|
const t = this;
|
||||||
this.RightColumn.ready((cls, instances) => {
|
this.RightColumn.ready((cls, instances) => {
|
||||||
cls.prototype.ffzHideOnBreakpoint = function() {
|
cls.prototype.ffzHideOnBreakpoint = function() {
|
||||||
|
@ -196,6 +210,12 @@ export default class Layout extends Module {
|
||||||
return this.settings.get('layout.is-minimal')
|
return this.settings.get('layout.is-minimal')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updatePopular(inst) {
|
||||||
|
const node = this.fine.getChildNode(inst);
|
||||||
|
if ( node )
|
||||||
|
node.classList.add('ffz--popular-channels');
|
||||||
|
}
|
||||||
|
|
||||||
updatePortraitMode() {
|
updatePortraitMode() {
|
||||||
for(const inst of this.RightColumn.instances)
|
for(const inst of this.RightColumn.instances)
|
||||||
inst.hideOnBreakpoint();
|
inst.hideOnBreakpoint();
|
||||||
|
|
|
@ -33,21 +33,38 @@ export default class Switchboard extends Module {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
awaitRoute(count = 0) {
|
||||||
|
const route = this.fine.searchTree(null,
|
||||||
|
n => n.props && n.props.component && n.props.path,
|
||||||
|
100);
|
||||||
|
|
||||||
|
if ( route )
|
||||||
|
return Promise.resolve(route);
|
||||||
|
|
||||||
|
if ( count > 50 )
|
||||||
|
return Promise.resolve(null);
|
||||||
|
|
||||||
|
return new Promise(r => setTimeout(r, 50)).then(() => this.awaitRoute(count + 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
async onEnable() {
|
async onEnable() {
|
||||||
await this.parent.awaitElement('.twilight-minimal-root,.twilight-root,#root>div');
|
await this.parent.awaitElement('.twilight-minimal-root,.twilight-root,#root>div');
|
||||||
if ( this.web_munch._require || this.web_munch.v4 === false )
|
if ( this.web_munch._require || this.web_munch.v4 === false )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const router = await this.awaitRouter(),
|
// Find the current route.
|
||||||
da_switch = router && this.fine.searchTree(router, n => n.context && n.context.router && n.props && n.props.children && n.componentWillMount && n.componentWillMount.toString().includes('Switch'));
|
const route = await this.awaitRoute(),
|
||||||
|
da_switch = route && this.fine.searchParent(route, n => n.props && n.props.children);
|
||||||
|
|
||||||
if ( ! da_switch )
|
if ( ! da_switch )
|
||||||
return new Promise(r => setTimeout(r, 50)).then(() => this.onEnable());
|
return new Promise(r => setTimeout(r, 50)).then(() => this.onEnable());
|
||||||
|
|
||||||
// Identify Router
|
// Identify Router
|
||||||
this.log.info(`Found Router and Switch with ${da_switch.props.children.length} routes.`);
|
const router = await this.awaitRouter();
|
||||||
|
|
||||||
const location = da_switch.context.router.route.location.pathname;
|
this.log.info(`Found Route and Switch with ${da_switch.props.children.length} routes.`);
|
||||||
|
const location = router.props.location.pathname;
|
||||||
|
|
||||||
for(const route of da_switch.props.children) {
|
for(const route of da_switch.props.children) {
|
||||||
if ( ! route.props || ! route.props.component )
|
if ( ! route.props || ! route.props.component )
|
||||||
|
|
144
src/std-components/icon-picker.vue
Normal file
144
src/std-components/icon-picker.vue
Normal file
|
@ -0,0 +1,144 @@
|
||||||
|
<template lang="html">
|
||||||
|
<div class="ffz--icon-picker">
|
||||||
|
<div class="tw-full-width">
|
||||||
|
<div class="tw-search-input">
|
||||||
|
<label for="icon-search" class="tw-hide-accessible">
|
||||||
|
{{ t('setting.icon.search', 'Search for Icon') }}
|
||||||
|
</label>
|
||||||
|
<div class="tw-relative tw-mg-t-05">
|
||||||
|
<div class="tw-absolute tw-align-items-center tw-c-text-alt-2 tw-flex tw-full-height tw-input__icon tw-justify-content-center tw-left-0 tw-top-0 tw-z-default">
|
||||||
|
<figure class="ffz-i-search" />
|
||||||
|
</div>
|
||||||
|
<input
|
||||||
|
id="icon-search"
|
||||||
|
:placeholder="t('setting.actions.icon.search', 'Search for Icon')"
|
||||||
|
v-model="search"
|
||||||
|
type="search"
|
||||||
|
class="tw-block tw-border-radius-medium tw-font-size-6 tw-full-width tw-input tw-pd-l-3 tw-pd-r-1 tw-pd-y-05"
|
||||||
|
autocapitalize="off"
|
||||||
|
autocorrect="off"
|
||||||
|
autocomplete="off"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<simplebar classes="tw-c-background-alt-2 tw-border-l tw-border-r tw-border-b ffz--icon-picker__list tw-mg-b-05">
|
||||||
|
<div v-if="visible.length" role="radiogroup" class="tw-pd-1 tw-flex tw-flex-wrap" >
|
||||||
|
<div
|
||||||
|
v-for="i of visible"
|
||||||
|
:key="i[0]"
|
||||||
|
:aria-checked="value.icon === i[0]"
|
||||||
|
:class="{'tw-interactable--selected': value.icon === i[0]}"
|
||||||
|
class="ffz-icon tw-interactable tw-interactable--inverted"
|
||||||
|
role="radio"
|
||||||
|
tabindex="0"
|
||||||
|
@keydown.space.stop.prevent=""
|
||||||
|
@keyup.space="change(i[0])"
|
||||||
|
@keyup.enter="change(i[0])"
|
||||||
|
@click="change(i[0])"
|
||||||
|
>
|
||||||
|
<figure :class="`tw-mg-y-05 tw-mg-x-1 ${i[0]}`" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-else class="tw-align-center tw-pd-1 tw-c-text-alt-2">
|
||||||
|
{{ t('setting.actions.empty-search', 'no results') }}
|
||||||
|
</div>
|
||||||
|
</simplebar>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
import {escape_regex, deep_copy} from 'utilities/object';
|
||||||
|
import {load, ICONS as FA_ICONS, ALIASES as FA_ALIASES} from 'utilities/font-awesome';
|
||||||
|
|
||||||
|
const FFZ_ICONS = [
|
||||||
|
'zreknarf',
|
||||||
|
'crown',
|
||||||
|
'verified',
|
||||||
|
'inventory',
|
||||||
|
'ignore',
|
||||||
|
'pin-outline',
|
||||||
|
'pin',
|
||||||
|
'block',
|
||||||
|
'ok',
|
||||||
|
'clock',
|
||||||
|
'eye',
|
||||||
|
'eye-off',
|
||||||
|
'trash',
|
||||||
|
'discord',
|
||||||
|
'star',
|
||||||
|
'star-empty',
|
||||||
|
'twitch',
|
||||||
|
'twitter',
|
||||||
|
'download',
|
||||||
|
'upload',
|
||||||
|
'download-cloud',
|
||||||
|
'upload-cloud',
|
||||||
|
'tag',
|
||||||
|
'tags',
|
||||||
|
'retweet',
|
||||||
|
'thumbs-up',
|
||||||
|
'thumbs-down',
|
||||||
|
'bell',
|
||||||
|
'bell-off',
|
||||||
|
'pencil',
|
||||||
|
'info',
|
||||||
|
'help',
|
||||||
|
'calendar',
|
||||||
|
'lock',
|
||||||
|
'lock-open',
|
||||||
|
'arrows-cw',
|
||||||
|
'gift',
|
||||||
|
'eyedropper',
|
||||||
|
'github',
|
||||||
|
'user-secret'
|
||||||
|
];
|
||||||
|
|
||||||
|
const FFZ_ALIASES = {
|
||||||
|
'block': ['ban', 'block'],
|
||||||
|
'ok': ['ok', 'unban', 'untimeout', 'checkmark'],
|
||||||
|
'clock': ['clock', 'clock-o', 'timeout']
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const ICONS = FFZ_ICONS
|
||||||
|
.map(x => [`ffz-i-${x}`, FFZ_ALIASES[x] ? FFZ_ALIASES[x].join(' ') : x])
|
||||||
|
.concat(FA_ICONS.filter(x => ! FFZ_ICONS.includes(x)).map(x => [`ffz-fa fa-${x}`, FA_ALIASES[x] ? FA_ALIASES[x].join(' ') : x]));
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: ['value'],
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
search: '',
|
||||||
|
icons: deep_copy(ICONS)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
visible() {
|
||||||
|
if ( ! this.search || ! this.search.length )
|
||||||
|
return this.icons;
|
||||||
|
|
||||||
|
const search = this.search.toLowerCase().replace(' ', '-'),
|
||||||
|
reg = new RegExp('(?:^|-| )' + escape_regex(search), 'i');
|
||||||
|
|
||||||
|
return this.icons.filter(x => reg.test(x[1]));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
load();
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
change(val) {
|
||||||
|
this.value.icon = val;
|
||||||
|
this.$emit('input', this.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
Loading…
Add table
Add a link
Reference in a new issue