1
0
Fork 0
mirror of https://github.com/FrankerFaceZ/FrankerFaceZ.git synced 2025-08-11 00:20:54 +00:00
* Added: Settings page for editing the list of categories that are Blocked or have Hidden Thumbnails.
* Changed: When popping out the FFZ Control Center, it now remembers which page it was on.
* Fixed: Emoji-related features not functioning correctly if the emoji style is set to an invalid value. (Closes #923)
* Fixed: Messages not being highlighted when a viewer card is open. (Closes #920)
* Fixed: Disable minimal navigation for Watch Parties. (Closes #916)
* Fixed: Block and Hide Thumbnails controls not appearing on appropriate directory pages.
* Fixed: Hide Thumbnails not working for a category when on that category's directory page.
* Fixed: Move up theater metadata to avoid it overlapping with the seek preview when viewing videos.
* Fixed: Various instances of `occurred` being spelled incorrectly.
* Fixed: Setting incorrectly named `Show Twitch Prime offers.` has been renamed to `Show Prime Gaming Loot.` to reflect Amazon's branding changes.
* API Added: `ProviderMixin` for creating Vue components for settings that are set directly to the settings provider. See the new `games-list-editor` for an example.
* API Added: Select box and combo box settings now have an optional `no_i18n` property to avoid i18n keys being automatically generated for their entries.
This commit is contained in:
SirStendec 2020-10-14 14:55:10 -04:00
parent 53b22d8a09
commit b88e6f0f75
17 changed files with 343 additions and 31 deletions

View file

@ -78,6 +78,11 @@ export default class Emoji extends Module {
this.settings.add('chat.emoji.style', {
default: 'twitter',
process(ctx, val) {
if ( val != 0 && ! IMAGE_PATHS[val] )
return 'twitter';
return val;
},
ui: {
path: 'Chat > Appearance >> Emoji',
title: 'Emoji Style',

View file

@ -1665,12 +1665,12 @@ export default class Chat extends Module {
if ( data.error )
data = {
v: 5,
title: this.i18n.t('card.error', 'An error occured.'),
title: this.i18n.t('card.error', 'An error occurred.'),
description: data.error,
short: {
type: 'header',
image: {type: 'image', url: ERROR_IMAGE},
title: {type: 'i18n', key: 'card.error', phrase: 'An error occured.'},
title: {type: 'i18n', key: 'card.error', phrase: 'An error occurred.'},
subtitle: data.error
}
}

View file

@ -100,7 +100,7 @@ export default {
if ( ! response.ok ) {
this.uploading = false;
this.url = 'An error occured.';
this.url = 'An error occurred.';
}
this.url = await response.text();

View file

@ -91,7 +91,7 @@
<div class="tw-align-center tw-pd-1">
<div v-if="error">
{{ t('home.changelog.error', 'An error occured loading changes from GitHub.') }}
{{ t('home.changelog.error', 'An error occurred loading changes from GitHub.') }}
</div>
<h1 v-else-if="loading" class="tw-mg-5 ffz-i-zreknarf loading" />
<div v-else-if="! more">

View file

@ -0,0 +1,157 @@
<template lang="html">
<section class="ffz--widget ffz--game-list">
<div class="tw-align-items-center tw-flex tw-flex-nowrap tw-flex-row tw-full-width">
<div class="tw-flex-grow-1 tw-mg-r-05">
<autocomplete
v-slot="slot"
v-model="adding"
:input-id="'category$' + id"
:items="fetchCategories"
:suggest-on-focus="true"
:escape-to-clear="false"
class="tw-flex-grow-1"
>
<div class="tw-pd-x-1 tw-pd-y-05">
<div class="tw-card tw-relative">
<div class="tw-align-items-center tw-flex tw-flex-nowrap tw-flex-row">
<div class="tw-card-img tw-card-img--size-3 tw-flex-shrink-0 tw-overflow-hidden">
<aspect :ratio="1/1.33">
<img
:alt="slot.item.displayName"
:src="slot.item.boxArtURL"
class="tw-image"
>
</aspect>
</div>
<div class="tw-card-body tw-overflow-hidden tw-relative">
<p class="tw-pd-x-1">
{{ slot.item.displayName }}
</p>
</div>
</div>
</div>
</div>
</autocomplete>
</div>
<div class="tw-flex-shrink-0">
<button class="tw-button" @click="add">
<span class="tw-button__text">
{{ t('setting.terms.add-term', 'Add') }}
</span>
</button>
</div>
</div>
<div v-if="! value || ! value.length" class="tw-mg-t-05 tw-c-text-alt-2 tw-font-size-4 tw-align-center tw-c-text-alt-2 tw-pd-05">
{{ t('setting.no-items', 'no items') }}
</div>
<ul v-else class="ffz--term-list tw-mg-t-05">
<li
v-for="i in value"
:key="i"
class="ffz--term ffz--game-term tw-align-items-center tw-flex tw-flex-nowrap tw-flex-row tw-full-width"
>
<div class="tw-flex-grow-1 tw-mg-r-05">
<a
v-if="can_link"
:href="`/directory/game/${i}`"
class="tw-link"
@click.prevent="handleLink(i)"
>
{{ i }}
</a>
<span v-else>
{{ i }}
</span>
</div>
<div class="tw-flex-shrink-0">
<button class="tw-button tw-button--text tw-tooltip-wrapper" @click="remove(i)">
<span class="tw-button__text ffz-i-trash" />
<div class="tw-tooltip tw-tooltip--down tw-tooltip--align-right">
{{ t('setting.delete', 'Delete') }}
</div>
</button>
</div>
</li>
</ul>
</section>
</template>
<script>
import ProviderMixin from '../provider-mixin';
import {deep_copy} from 'utilities/object';
let last_id = 0;
export default {
mixins: [ProviderMixin],
props: ['item', 'context'],
data() {
return {
id: last_id++,
adding: '',
can_link: false
}
},
created() {
const ffz = this.context.getFFZ();
this.loader = ffz.resolve('site.twitch_data');
this.router = ffz.resolve('site.router');
this.can_link = ! ffz.resolve('main_menu').exclusive;
},
methods: {
add() {
if ( ! this.adding?.length )
return;
const values = Array.from(this.value);
if ( values.includes(this.adding) )
return;
values.push(this.adding);
this.set(values);
},
remove(item) {
const values = Array.from(this.value),
idx = values.indexOf(item);
if ( idx === -1 )
return;
if ( values.length === 1 )
this.clear();
else {
values.splice(idx, 1);
this.set(values);
}
},
handleLink(i) {
this.router.navigate('dir-game-index', {gameName: i});
},
async fetchCategories(query) {
if ( ! this.loader )
return [];
const data = await this.loader.getMatchingCategories(query);
if ( ! data || ! data.items )
return [];
return deep_copy(data.items);
},
onSelected(...args) {
console.log('selected', args);
}
}
}
</script>

View file

@ -10,7 +10,8 @@ import {get, has, deep_copy} from 'utilities/object';
import Dialog from 'utilities/dialog';
import Mixin from './setting-mixin';
import SettingMixin from './setting-mixin';
import ProviderMixin from './provider-mixin';
import {parse_path} from 'src/settings';
@ -32,7 +33,8 @@ export default class MainMenu extends Module {
this.load_requires = ['vue'];
this.Mixin = Mixin;
this.Mixin = this.SettingMixin = SettingMixin;
this.ProviderMixin = ProviderMixin;
//this.should_enable = true;
@ -134,9 +136,9 @@ export default class MainMenu extends Module {
this.scheduleUpdate();
}
openPopout() {
openPopout(item) {
const win = window.open(
'https://twitch.tv/popout/frankerfacez/chat?ffz-settings',
`https://twitch.tv/popout/frankerfacez/chat?ffz-settings${item ? `=${encodeURIComponent(item)}` : ''}`,
'_blank',
'resizable=yes,scrollbars=yes,width=850,height=600'
);
@ -633,6 +635,7 @@ export default class MainMenu extends Module {
const t = this,
Vue = this.vue.Vue,
settings = this.settings,
provider = settings.provider,
context = settings.main_context,
[profiles, profile_keys] = this.getProfiles();
@ -665,6 +668,16 @@ export default class MainMenu extends Module {
getFFZ: () => t.resolve('core'),
provider: {
unwrap: () => provider,
get: (...args) => provider.get(...args),
set: (...args) => provider.set(...args),
delete: (...args) => provider.delete(...args),
has: (...args) => provider.has(...args),
on: (...args) => provider.on(...args),
off: (...args) => provider.off(...args)
},
context: {
_users: 0,
@ -924,18 +937,18 @@ export default class MainMenu extends Module {
close: e => ! this.dialog.exclusive && this.dialog.toggleVisible(e),
popout: e => {
if ( this.dialog.exclusive )
return;
this.dialog.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.')); // eslint-disable-line no-alert
},
version: window.FrankerFaceZ.version_info,
};
out.popout = e => {
if ( this.dialog.exclusive )
return;
this.dialog.toggleVisible(e);
if ( ! this.openPopout(out.currentItem?.full_key) )
alert(this.i18n.t('popup.error', 'We tried opening a pop-up window and could not. Make sure to allow pop-ups from Twitch.')); // eslint-disable-line no-alert
}
return out;
}
}

View file

@ -0,0 +1,108 @@
'use strict';
import {deep_copy} from 'utilities/object';
export default {
data() {
return {
value: undefined,
has_value: false,
_unseen: false
}
},
created() {
const provider = this.context.provider,
setting = this.item.setting;
provider.on('changed', this._providerChange, this);
this.has_value = provider.has(setting);
if ( this.has_value )
this.value = deep_copy(provider.get(setting));
else
this.value = this.default_value;
if ( this.item.unseen ) {
this._unseen = true;
this.item.unseen = 0;
}
},
destroyed() {
const provider = this.context.provider;
provider.off('changed', this._providerChange, this);
this.value = undefined;
this.has_value = false;
},
computed: {
data() {
const data = this.item.data;
if ( typeof data === 'function' )
return data(this.value);
return data;
},
unseen() {
return this._unseen || this.item.unseen > 0;
},
default_value() {
if ( typeof this.item.default === 'function' )
return this.item.default(this.context);
return deep_copy(this.item.default);
},
isDefault() {
return ! this.has_value
}
},
methods: {
_providerChange(key, val, deleted) {
if ( key !== this.item.setting )
return;
if ( deleted ) {
this.has_value = false;
this.value = this.default_value;
} else {
this.has_value = true;
this.value = deep_copy(val);
}
},
set(value) {
if ( this.item.process )
value = this.item.process(value);
const provider = this.context.provider,
setting = this.item.setting;
provider.set(setting, value);
this.has_value = true;
this.value = deep_copy(value);
if ( this.item.onUIChange )
this.item.onUIChange(this.value);
},
clear() {
const provider = this.context.provider,
setting = this.item.setting;
provider.delete(setting);
this.value = this.default_value;
this.has_value = false;
if ( this.item.onUIChange )
this.item.onUIChange(this.value);
}
}
}