1
0
Fork 0
mirror of https://github.com/FrankerFaceZ/FrankerFaceZ.git synced 2025-08-23 06:20:54 +00:00

4.0.0-rc4.3

* Added: Settings Search
* Added: Button to open the FFZ Control Center in a new window.
* Added: Button to fade the FFZ Control Center.
This commit is contained in:
SirStendec 2018-07-05 20:27:17 -04:00
parent a23bc74ae4
commit 0775cd1e77
23 changed files with 214 additions and 53 deletions

View file

@ -100,7 +100,7 @@ class FrankerFaceZ extends Module {
FrankerFaceZ.Logger = Logger;
const VER = FrankerFaceZ.version_info = {
major: 4, minor: 0, revision: 0, extra: '-rc4.2',
major: 4, minor: 0, revision: 0, extra: '-rc4.3',
build: __webpack_hash__,
toString: () =>
`${VER.major}.${VER.minor}.${VER.revision}${VER.extra || ''}${DEBUG ? '-dev' : ''}`

View file

@ -67,7 +67,9 @@ const FFZ_ICONS = [
'pin',
'block',
'ok',
'clock'
'clock',
'eye',
'eye-off'
];
const FFZ_ALIASES = {

View file

@ -41,7 +41,7 @@ export default class Actions extends Module {
type: 'array_merge',
ui: {
path: 'Chat > In-Line Actions',
path: 'Chat > In-Line Actions @{"description": "Here, you can define custom actions that will appear along messages in chat. If you aren\'t seeing an action you\'ve defined here in chat, please make sure that you have enabled Mod Icons in the chat settings menu."}',
component: 'chat-actions',
inline: true,

View file

@ -151,6 +151,7 @@ export default class Badges extends Module {
type: 'object_merge',
ui: {
path: 'Chat > Badges >> tabs ~> Visibility',
title: 'Visibility',
component: 'badge-visibility',
data: () => {
const twitch = [],

View file

@ -28,7 +28,6 @@
<ul class="tw-mg-b-2">
<li>Settings from the old version are not being imported.</li>
<li>Settings cannot be searched.</li>
<li>Advanced input (better tab completion, history) isn't available.</li>
</ul>

View file

@ -1,23 +1,44 @@
<template lang="html">
<div
:class="{ maximized: maximized || exclusive, exclusive }"
:class="{ maximized: maximized || exclusive, exclusive, faded }"
class="ffz-main-menu tw-elevation-3 tw-c-background-alt tw-c-text tw-border tw-flex tw-flex-nowrap tw-flex-column"
>
<header class="tw-c-background tw-full-width tw-align-items-center tw-flex tw-flex-nowrap" @dblclick="resize">
<h3 class="ffz-i-zreknarf ffz-i-pd-1">FrankerFaceZ</h3>
<div class="tw-flex-grow-1 tw-pd-x-2">
<!--div class="tw-search-input">
<label for="ffz-main-menu.search" class="hide-accessible">{{ t('main-menu.search', 'Search Settings') }}</label>
<div class="relative">
<div class="tw-input__icon-group">
<div class="tw-input__icon">
<figure class="ffz-i-search" />
</div>
<div class="tw-search-input">
<label for="ffz-main-menu.search" class="tw-hide-accessible">{{ t('main-menu.search', 'Search Settings') }}</label>
<div class="tw-relative">
<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 type="search" class="tw-input tw-input--icon-left" :placeholder="t('main-menu.search', 'Search Settings')" autocapitalize="off" autocorrect="off" autocomplete="off" id="ffz-main-menu.search">
<input
id="ffz-main-menu.search"
v-model="query"
:placeholder="t('main-menu.search', 'Search Settings')"
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"
spellcheck="false"
>
</div>
</div-->
</div>
</div>
<button v-if="!maximized && !exclusive" class="tw-button-icon tw-mg-x-05" @click="faded = ! faded">
<span class="tw-button-icon__icon">
<figure :class="faded ? 'ffz-i-eye-off' : 'ffz-i-eye'" />
</span>
</button>
<button v-if="!exclusive" class="tw-button-icon tw-mg-x-05 tw-tooltip-wrapper" @click="popout">
<span class="tw-button-icon__icon">
<figure class="ffz-i-link-ext" />
</span>
<div class="tw-tooltip tw-tooltip--down tw-tooltip--align-center">
{{ t('main-menu.popout', 'Open Settings in a New Window') }}
</div>
</button>
<button v-if="!exclusive" class="tw-button-icon tw-mg-x-05" @click="resize">
<span class="tw-button-icon__icon">
<figure :class="{'ffz-i-window-maximize': !maximized, 'ffz-i-window-restore': maximized}" />
@ -42,6 +63,7 @@
<menu-tree
:current-item="currentItem"
:modal="nav"
:filter="filter"
@change-item="changeItem"
@navigate="navigate"
/>
@ -62,6 +84,7 @@
ref="page"
:context="context"
:item="currentItem"
:filter="filter"
@change-item="changeItem"
@navigate="navigate"
/>
@ -79,6 +102,12 @@ export default {
return this.$vnode.data;
},
computed: {
filter() {
return this.query.toLowerCase()
}
},
watch: {
maximized() {
this.updateDrag();

View file

@ -8,27 +8,45 @@
class="tw-pd-b-1"
v-html="t(item.desc_i18n_key, item.description, item)"
/>
<component
<div
v-for="i in item.contents"
:is="i.component"
:context="context"
:item="i"
:key="i.full_key"
/>
:class="{'ffz-unmatched-item': showing && ! shouldShow(i)}"
>
<component
:is="i.component"
:context="context"
:item="i"
:filter="filter"
/>
</div>
</div>
</template>
<script>
export default {
props: ['item', 'context'],
props: ['item', 'context', 'filter'],
computed: {
showing() {
return this.shouldShow(this.item);
},
classes() {
return [
'ffz--menu-container',
this.item.full_box ? 'tw-border' : 'tw-border-t'
]
}
},
methods: {
shouldShow(item) {
if ( ! this.filter || ! this.filter.length || ! item.search_terms )
return true;
return item.search_terms.includes(this.filter);
}
}
}
</script>

View file

@ -26,30 +26,40 @@
/>
<template v-if="! item.contents || ! item.contents.length">
<ul class="tw-border-t tw-pd-y-1">
<li v-for="i in item.items" :key="i.full_key" class="tw-pd-x-1">
<li
v-for="i in item.items"
:key="i.full_key"
:class="{'ffz-unmatched-item': ! shouldShow(i)}"
class="tw-pd-x-1"
>
<a href="#" @click="$emit('change-item', i, false)">
{{ t(i.i18n_key, i.title, i) }}
</a>
</li>
</ul>
</template>
<component
<div
v-for="i in item.contents"
ref="children"
:is="i.component"
:context="context"
:item="i"
:key="i.full_key"
@change-item="changeItem"
@navigate="navigate"
/>
:class="{'ffz-unmatched-item': ! shouldShow(i)}"
>
<component
ref="children"
:is="i.component"
:context="context"
:item="i"
:filter="filter"
@change-item="changeItem"
@navigate="navigate"
/>
</div>
</div>
</template>
<script>
export default {
props: ['item', 'context'],
props: ['item', 'context', 'filter'],
computed: {
breadcrumbs() {
@ -65,6 +75,13 @@ export default {
},
methods: {
shouldShow(item) {
if ( ! this.filter || ! this.filter.length || ! item.search_terms )
return true;
return item.search_terms.includes(this.filter);
},
changeItem(item) {
this.$emit('change-item', item);
},

View file

@ -12,6 +12,7 @@
>
<li
v-for="item in modal"
v-if="shouldShow(item)"
:key="item.full_key"
:class="[currentItem === item ? 'active' : '']"
role="presentation"
@ -34,7 +35,7 @@
<span class="tw-flex-grow-1">
{{ t(item.i18n_key, item.title, item) }}
</span>
<span v-if="item.pill" class="pill">
<span v-if="item.pill" class="tw-pill">
{{ item.pill_i18n_key ? t(item.pill_i18n_key, item.pill, item) : item.pill }}
</span>
</div>
@ -43,6 +44,7 @@
:root="item"
:current-item="currentItem"
:modal="item.items"
:filter="filter"
@change-item="i => $emit('change-item', i)"
/>
</li>
@ -82,7 +84,7 @@ function recursiveExpand(node) {
export default {
props: ['root', 'modal', 'currentItem'],
props: ['root', 'modal', 'currentItem', 'filter'],
computed: {
tabIndex() {
@ -91,6 +93,28 @@ export default {
},
methods: {
shouldShow(item) {
if ( ! this.filter || ! this.filter.length || this.containsCurrent(item) )
return true;
if ( item.search_terms && item.search_terms.includes(this.filter) )
return true;
return false;
},
containsCurrent(item) {
let i = this.currentItem;
while ( i ) {
if ( item === i )
return true;
i = i.parent;
}
return false;
},
clickItem(item) {
if ( ! item.expanded )
item.expanded = true;

View file

@ -65,6 +65,22 @@ export default class MainMenu extends Module {
}
openPopout() {
const win = window.open(
'https://twitch.tv/popout/frankerfacez/chat?ffz-settings',
'_blank',
'resizable=yes,scrollbars=yes,width=850,height=600'
);
if ( win ) {
win.focus();
return true;
} else {
this.log.warn('Unable to open popout settings window.');
return false;
}
}
async onLoad() {
this.vue.component(
(await import(/* webpackChunkName: "main-menu" */ './components.js')).default
@ -542,6 +558,9 @@ export default class MainMenu extends Module {
return {
context,
query: '',
faded: false,
nav: settings,
currentItem: settings.keys['home'], // settings[0],
nav_keys: settings.keys,
@ -549,6 +568,14 @@ export default class MainMenu extends Module {
maximized: this._maximized,
resize: e => !this.exclusive && this.toggleSize(e),
close: e => !this.exclusive && this.toggleVisible(e),
popout: e => {
if ( this.exclusive )
return;
this.toggleVisible(e);
if ( ! this.openPopout() )
alert(this.i18n.t('popup.error', 'We tried opening a pop-up window and could not. Make sure to allow pop-ups from Twitch.'));
},
version: window.FrankerFaceZ.version_info,
exclusive: this.exclusive

View file

@ -175,8 +175,8 @@ export default class Directory extends SiteModule {
channel_url = `/${channel.login}`,
game_url = game && `/directory/game/${stream.game.name}`,
user_link = <a href={channel_url} data-href={channel_url} onClick={t.routeClick} class="tw-link tw-link--inherit">{channel.displayName}</a>,
game_link = game && <a href={game_url} data-href={game_url} onClick={t.routeClick} class="tw-link tw-link--inherit">{game.name}</a>;
user_link = <a href={channel_url} data-href={channel_url} onClick={t.routeClick} title={channel.displayName} class="tw-link tw-link--inherit">{channel.displayName}</a>,
game_link = game && <a href={game_url} data-href={game_url} onClick={t.routeClick} title={game.name} class="tw-link tw-link--inherit">{game.name}</a>;
return (<div>
<a href={channel_url} data-href={channel_url} onClick={t.routeClick} class="tw-link tw-link--inherit" data-test-selector="preview-card-titles__primary-link">
@ -203,7 +203,7 @@ export default class Directory extends SiteModule {
nodes.length > 1 ?
t.i18n.t('directory.hosted.by-many', 'Hosted by %{count} channel%{count|en_plural}', nodes.length) :
t.i18n.tList('directory.hosted.by-one', 'Hosted by %{user}', {
user: <a href={`/${nodes[0].login}`} data-href={`/${nodes[0].login}`} onClick={t.routeClick} class="tw-link tw-link--inherit">{nodes[0].displayName}</a>
user: <a href={`/${nodes[0].login}`} data-href={`/${nodes[0].login}`} onClick={t.routeClick} title={nodes[0].displayName} class="tw-link tw-link--inherit">{nodes[0].displayName}</a>
})
}</p>
</div>

View file

@ -41,6 +41,7 @@
}
.ffz-featured-follow,
.ffz-auto-host-options {
.ffz-channel-avatar {
max-width: 3.2rem;

View file

@ -15,6 +15,10 @@
> header {
cursor: move;
}
&.faded {
opacity: 0.5
}
}
&.maximized {

View file

@ -24,7 +24,7 @@
:id="'tab-for-' + i.full_key"
:aria-selected="selected === idx"
:aria-controls="'tab-panel-' + i.full_key"
:class="[selected === idx ? 'active' : '']"
:class="{'active': selected === idx, 'ffz-unmatched-item': showing && ! shouldShow(i)}"
role="tab"
class="tab tw-pd-y-05 tw-pd-x-1"
@click="selected = idx"
@ -43,22 +43,27 @@
<section v-if="tab.description" class="tw-pd-b-1">
{{ t(tab.desc_i18n_key, tab.description, tab) }}
</section>
<component
<div
v-for="i in tab.contents"
:is="i.component"
:current-profile="currentProfile"
:profiles="profiles"
:context="context"
:item="i"
:key="i.full_key"
/>
:class="{'ffz-unmatched-item': showing && ! shouldShow(i)}"
>
<component
:is="i.component"
:current-profile="currentProfile"
:profiles="profiles"
:context="context"
:item="i"
:filter="filter"
/>
</div>
</section>
</div>
</template>
<script>
export default {
props: ['item', 'profiles', 'currentProfile', 'context'],
props: ['item', 'profiles', 'currentProfile', 'context', 'filter'],
data() {
return {
@ -67,6 +72,10 @@ export default {
},
computed: {
showing() {
return this.shouldShow(this.item)
},
tab() {
return this.item.tabs[this.selected];
}
@ -113,6 +122,13 @@ export default {
nextTab() {
if ( this.selected + 1 < this.item.tabs.length )
this.selected++;
},
shouldShow(item) {
if ( ! this.filter || ! this.filter.length || ! item.search_terms )
return true;
return item.search_terms.includes(this.filter);
}
}
}

View file

@ -146,6 +146,21 @@ export function filter_match(filter, target) {
}
export function substr_count(str, needle) {
let i = 0, idx = 0;
while( idx < str.length ) {
const x = str.indexOf(needle, idx);
if ( x === -1 )
break;
i++;
idx = x + 1;
}
return i;
}
/**
* Get a value from an object at a path.
* @param {string|Array} path The path to follow, using periods to go down a level.