1
0
Fork 0
mirror of https://github.com/FrankerFaceZ/FrankerFaceZ.git synced 2025-06-27 21:05:53 +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

@ -1,3 +1,10 @@
<div class="list-header">4.0.0-rc4.3<span>@46f98c4cd4559eaa9828</span> <time datetime="2018-07-05">(2018-07-05)</time></div>
<ul class="chat-menu-content menu-side-padding">
<li>Added: Settings Search</li>
<li>Added: Button to open the FrankerFaceZ Control Center in a new window.</li>
<li>Added: Button to fade the FrankerFaceZ Control Center.</li>
</ul>
<div class="list-header">4.0.0-rc4.2<span>@bfc82fd92a39871e75ff</span> <time datetime="2018-07-04">(2018-07-04)</time></div>
<ul class="chat-menu-content menu-side-padding">
<li>Fixed: Issue scrolling up with Smooth Scrolling for Chat enabled without Freeze on Hover.</li>

Binary file not shown.

View file

@ -88,6 +88,10 @@
<glyph glyph-name="views" unicode="&#xe828;" d="M688 38h-375l-250 250v62 63l250 250h375l250-250v-63-62l-250-250z m-188 500c-103 0-187-84-187-188 0-103 84-187 187-187 104 0 188 84 188 187 0 104-84 188-188 188z m0-250c-35 0-62 28-62 62s27 63 62 63 63-28 63-63-28-62-63-62z" horiz-adv-x="1000" />
<glyph glyph-name="eye" unicode="&#xe829;" d="M929 314q-85 132-213 197 34-58 34-125 0-103-73-177t-177-73-177 73-73 177q0 67 34 125-128-65-213-197 75-114 187-182t242-68 243 68 186 182z m-402 215q0 11-8 19t-19 7q-70 0-120-50t-50-119q0-11 8-19t19-8 19 8 8 19q0 48 34 82t82 34q11 0 19 8t8 19z m473-215q0-19-11-38-78-129-210-206t-279-77-279 77-210 206q-11 19-11 38t11 39q78 128 210 205t279 78 279-78 210-205q11-20 11-39z" horiz-adv-x="1000" />
<glyph glyph-name="eye-off" unicode="&#xe82a;" d="M310 105l43 79q-48 35-76 88t-27 114q0 67 34 125-128-65-213-197 94-144 239-209z m217 424q0 11-8 19t-19 7q-70 0-120-50t-50-119q0-11 8-19t19-8 19 8 8 19q0 48 34 82t82 34q11 0 19 8t8 19z m202 106q0-4 0-5-59-105-176-316t-176-316l-28-50q-5-9-15-9-7 0-75 39-9 6-9 16 0 7 25 49-80 36-147 96t-117 137q-11 17-11 38t11 39q86 131 212 207t277 76q50 0 100-10l31 54q5 9 15 9 3 0 10-3t18-9 18-10 18-10 10-7q9-5 9-15z m21-249q0-78-44-142t-117-91l157 280q4-25 4-47z m250-72q0-19-11-38-22-36-61-81-84-96-194-149t-234-53l41 74q119 10 219 76t169 171q-65 100-158 164l35 63q53-36 102-85t81-103q11-19 11-39z" horiz-adv-x="1000" />
<glyph glyph-name="link-ext" unicode="&#xf08e;" d="M786 332v-178q0-67-47-114t-114-47h-464q-67 0-114 47t-47 114v464q0 66 47 113t114 48h393q7 0 12-5t5-13v-36q0-8-5-13t-12-5h-393q-37 0-63-26t-27-63v-464q0-37 27-63t63-27h464q37 0 63 27t26 63v178q0 8 5 13t13 5h36q8 0 13-5t5-13z m214 482v-285q0-15-11-25t-25-11-25 11l-98 98-364-364q-5-6-13-6t-12 6l-64 64q-6 5-6 12t6 13l364 364-98 98q-11 11-11 25t11 25 25 11h285q15 0 25-11t11-25z" horiz-adv-x="1000" />
<glyph glyph-name="twitter" unicode="&#xf099;" d="M904 622q-37-54-90-93 0-8 0-23 0-73-21-145t-64-139-103-117-144-82-181-30q-151 0-276 81 19-2 43-2 126 0 224 77-59 1-105 36t-64 89q19-3 34-3 24 0 48 6-63 13-104 62t-41 115v2q38-21 82-23-37 25-59 64t-22 86q0 49 25 91 68-83 164-133t208-55q-5 21-5 41 0 75 53 127t127 53q79 0 132-57 61 12 115 44-21-64-80-100 52 6 104 28z" horiz-adv-x="928.6" />

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Before After
Before After

Binary file not shown.

Binary file not shown.

Binary file not shown.

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.

View file

@ -51,7 +51,6 @@
}
.ffz-i-zreknarf:before {
content: '\e801'; /* '' */
width: 1.3em;
margin: .5rem .05rem 0;
}
@ -64,6 +63,7 @@
}
.ffz-i-cancel:before { content: '\e800'; } /* '' */
.ffz-i-zreknarf:before { content: '\e801'; } /* '' */
.ffz-i-search:before { content: '\e802'; } /* '' */
.ffz-i-clock:before { content: '\e803'; } /* '' */
.ffz-i-star:before { content: '\e804'; } /* '' */
@ -103,6 +103,8 @@
.ffz-i-pin-outline:before { content: '\e826'; } /* '' */
.ffz-i-gift:before { content: '\e827'; } /* '' */
.ffz-i-views:before { content: '\e828'; } /* '' */
.ffz-i-eye:before { content: '\e829'; } /* '' */
.ffz-i-eye-off:before { content: '\e82a'; } /* '' */
.ffz-i-link-ext:before { content: '\f08e'; } /* '' */
.ffz-i-twitter:before { content: '\f099'; } /* '' */
.ffz-i-gauge:before { content: '\f0e4'; } /* '' */

View file

@ -13,14 +13,9 @@
.tw-display-inline { display: inline !important }
.tw-width-auto { width: auto !important }
.ffz-monospace {
font-family: monospace;
}
.ffz-bottom-100 {
bottom: 100%;
}
.ffz-unmatched-item { opacity: 0.25 }
.ffz-monospace { font-family: monospace }
.ffz-bottom-100 { bottom: 100% }
.ffz--widget {