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;
|
||||
|
||||
const VER = FrankerFaceZ.version_info = {
|
||||
major: 4, minor: 2, revision: 5,
|
||||
major: 4, minor: 2, revision: 6,
|
||||
commit: __git_commit__,
|
||||
build: __webpack_hash__,
|
||||
toString: () =>
|
||||
|
|
|
@ -11,11 +11,11 @@ import {has} from 'utilities/object';
|
|||
const CLASSES = {
|
||||
'top-discover': '.top-nav__nav-link[data-a-target="discover-link"]',
|
||||
'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-friends': '.side-nav .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/"]',
|
||||
|
||||
'prime-offers': '.top-nav__prime',
|
||||
|
@ -77,7 +77,7 @@ export default class CSSTweaks extends Module {
|
|||
default: 1,
|
||||
ui: {
|
||||
path: 'Appearance > Layout >> Side Navigation',
|
||||
title: 'Display Recommended Channels',
|
||||
title: 'Display Recommended / Popular Channels',
|
||||
component: 'setting-select-box',
|
||||
data: [
|
||||
{value: 0, title: 'Never'},
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
// ============================================================================
|
||||
|
||||
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 MINIMAL_ROUTES = ['popout', 'embed-chat'];
|
||||
|
@ -24,6 +25,11 @@ export default class Layout extends Module {
|
|||
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', {
|
||||
default: false,
|
||||
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-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;
|
||||
this.RightColumn.ready((cls, instances) => {
|
||||
cls.prototype.ffzHideOnBreakpoint = function() {
|
||||
|
@ -196,6 +210,12 @@ export default class Layout extends Module {
|
|||
return this.settings.get('layout.is-minimal')
|
||||
}
|
||||
|
||||
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();
|
||||
|
|
|
@ -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() {
|
||||
await this.parent.awaitElement('.twilight-minimal-root,.twilight-root,#root>div');
|
||||
if ( this.web_munch._require || this.web_munch.v4 === false )
|
||||
return;
|
||||
|
||||
const router = await this.awaitRouter(),
|
||||
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'));
|
||||
// Find the current route.
|
||||
const route = await this.awaitRoute(),
|
||||
da_switch = route && this.fine.searchParent(route, n => n.props && n.props.children);
|
||||
|
||||
if ( ! da_switch )
|
||||
return new Promise(r => setTimeout(r, 50)).then(() => this.onEnable());
|
||||
|
||||
// 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) {
|
||||
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