mirror of
https://github.com/FrankerFaceZ/FrankerFaceZ.git
synced 2025-08-06 22:30:57 +00:00
4.14.11
* Added: `Current Category` rule type for profile rules. (Ironically, does not currently apply when on the directory page for a category.) * Fixed: Various chat input related features breaking, including Room Actions and tab-completion of emotes. * Fixed: Alignment of certain chat settings menu elements. * Maintenance: Update our copy of the `CHAT_TYPES` enum with new types from Twitch's client.
This commit is contained in:
parent
bbaf96584c
commit
903b0b234c
9 changed files with 199 additions and 8 deletions
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "frankerfacez",
|
||||
"author": "Dan Salvato LLC",
|
||||
"version": "4.14.10",
|
||||
"version": "4.14.11",
|
||||
"description": "FrankerFaceZ is a Twitch enhancement suite.",
|
||||
"license": "Apache-2.0",
|
||||
"scripts": {
|
||||
|
|
138
src/settings/components/category.vue
Normal file
138
src/settings/components/category.vue
Normal file
|
@ -0,0 +1,138 @@
|
|||
<template>
|
||||
<section class="tw-flex-grow-1 tw-align-self-start">
|
||||
<div class="tw-flex tw-align-items-center">
|
||||
<label :for="'category$' + id">
|
||||
{{ t(type.i18n, type.title) }}
|
||||
</label>
|
||||
|
||||
<div class="ffz--search-avatar tw-mg-x-05 tw-card-img--size-2">
|
||||
<aspect :ratio="1/1.33">
|
||||
<img
|
||||
v-if="current"
|
||||
:alt="current.displayName || current.name"
|
||||
:src="current.boxArtURL"
|
||||
class="tw-avatar__img tw-image"
|
||||
>
|
||||
</aspect>
|
||||
</div>
|
||||
|
||||
<autocomplete
|
||||
v-slot="slot"
|
||||
:input-id="'category$' + id"
|
||||
:items="fetchCategories"
|
||||
:value="search"
|
||||
:suggest-on-focus="true"
|
||||
:escape-to-clear="false"
|
||||
class="tw-flex-grow-1"
|
||||
@selected="onSelected"
|
||||
>
|
||||
<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>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import {debounce, deep_copy} from 'utilities/object';
|
||||
|
||||
let last_id = 0;
|
||||
|
||||
export default {
|
||||
props: ['value', 'type', 'filters', 'context'],
|
||||
|
||||
data() {
|
||||
return {
|
||||
id: last_id++,
|
||||
current: null,
|
||||
loaded_id: null
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
search() {
|
||||
return this.current && this.current.displayName || this.value.data.name;
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
value: {
|
||||
handler() {
|
||||
this.cacheCategory();
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
|
||||
created() {
|
||||
const ffz = FrankerFaceZ.get();
|
||||
this.loader = ffz.resolve('site.twitch_data');
|
||||
this.cacheCategory = debounce(this.cacheCategory, 50);
|
||||
},
|
||||
|
||||
beforeDestroy() {
|
||||
this.cacheCategory = null;
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.cacheCategory();
|
||||
},
|
||||
|
||||
methods: {
|
||||
async cacheCategory() {
|
||||
if ( ! this.loader || this.loaded_id === this.value.data.id )
|
||||
return;
|
||||
|
||||
this.current = null;
|
||||
this.loaded_id = this.value.data.id;
|
||||
|
||||
if ( ! this.loaded_id )
|
||||
return;
|
||||
|
||||
const data = await this.loader.getCategory(this.loaded_id);
|
||||
if ( data )
|
||||
this.current = deep_copy(data);
|
||||
else
|
||||
this.current = null;
|
||||
},
|
||||
|
||||
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(item) {
|
||||
this.current = item;
|
||||
this.value.data.name = item?.name || null;
|
||||
this.value.data.id = item?.id || null;
|
||||
this.value.data.boxArtURL = item?.boxArtURL || null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
|
@ -142,7 +142,6 @@ export const Time = {
|
|||
editor: () => import(/* webpackChunkName: 'main-menu' */ './components/time.vue')
|
||||
}
|
||||
|
||||
|
||||
export const TheaterMode = {
|
||||
createTest(config) {
|
||||
return ctx => ctx.ui && ctx.ui.theatreModeEnabled === config;
|
||||
|
@ -262,3 +261,25 @@ export const Channel = {
|
|||
}),
|
||||
editor: () => import(/* webpackChunkName: 'main-menu' */ './components/channel.vue')
|
||||
};
|
||||
|
||||
export const Category = {
|
||||
createTest(config = {}) {
|
||||
const name = config.name,
|
||||
id = config.id;
|
||||
|
||||
if ( ! id || ! name )
|
||||
return () => false;
|
||||
|
||||
return ctx => ctx.categoryID === id || (ctx.categoryID == null && ctx.category === name);
|
||||
},
|
||||
|
||||
title: 'Current Category',
|
||||
i18n: 'settings.filter.category',
|
||||
|
||||
default: () => ({
|
||||
name: null,
|
||||
id: null
|
||||
}),
|
||||
|
||||
editor: () => import(/* webpackChunkName: 'main-menu' */ './components/category.vue')
|
||||
}
|
|
@ -85,23 +85,36 @@ export default class Channel extends Module {
|
|||
});
|
||||
|
||||
this.ChannelPage.on('mount', inst => {
|
||||
const category = get('state.video.game', inst) || get('state.clip.game', inst) || get('state.channel.broadcastSettings.game', inst);
|
||||
|
||||
this.settings.updateContext({
|
||||
channel: get('state.channel.login', inst),
|
||||
channelID: get('state.channel.id', inst)
|
||||
channelID: get('state.channel.id', inst),
|
||||
channelColor: get('state.primaryColorHex', inst),
|
||||
category: category?.name,
|
||||
categoryID: category?.id
|
||||
});
|
||||
});
|
||||
|
||||
this.ChannelPage.on('unmount', () => {
|
||||
this.settings.updateContext({
|
||||
channel: null,
|
||||
channelID: null
|
||||
channelID: null,
|
||||
channelColor: null,
|
||||
category: null,
|
||||
categoryID: null
|
||||
});
|
||||
});
|
||||
|
||||
this.ChannelPage.on('update', inst => {
|
||||
const category = get('state.video.game', inst) || get('state.clip.game', inst) || get('state.channel.broadcastSettings.game', inst);
|
||||
|
||||
this.settings.updateContext({
|
||||
channel: get('state.channel.login', inst),
|
||||
channelID: get('state.channel.id', inst)
|
||||
channelID: get('state.channel.id', inst),
|
||||
channelColor: get('state.primaryColorHex', inst),
|
||||
category: category?.name,
|
||||
categoryID: category?.id
|
||||
});
|
||||
|
||||
if ( this.settings.get('channel.hosting.enable') || has(inst.state, 'hostMode') || has(inst.state, 'hostedChannel') )
|
||||
|
|
|
@ -110,6 +110,8 @@ const CHAT_TYPES = make_enum(
|
|||
'RewardGift',
|
||||
'SubMysteryGift',
|
||||
'AnonSubMysteryGift',
|
||||
'StandardPayForward',
|
||||
'CommunityPayForward',
|
||||
'FirstCheerMessage',
|
||||
'BitsBadgeTierMessage',
|
||||
'InlinePrivateCallout',
|
||||
|
|
|
@ -85,7 +85,7 @@ export default class Input extends Module {
|
|||
|
||||
this.ChatInput = this.fine.define(
|
||||
'chat-input',
|
||||
n => n && n.setChatInputRef && n.setLocalAutocompleteInputRef,
|
||||
n => n && n.setLocalChatInputRef && n.setLocalAutocompleteInputRef,
|
||||
Twilight.CHAT_ROUTES
|
||||
);
|
||||
|
||||
|
|
|
@ -110,7 +110,7 @@ export default class SettingsMenu extends Module {
|
|||
if ( ! this.ffzPauseClick )
|
||||
this.ffzPauseClick = () => this.setState({ffzPauseMenu: ! this.state.ffzPauseMenu});
|
||||
|
||||
return (<div class="tw-absolute tw-balloon tw-balloon--auto tw-balloon--up tw-block" data-a-target="chat-settings-balloon">
|
||||
return (<div class="tw-absolute tw-balloon tw-balloon--auto tw-balloon--right tw-balloon--up tw-block" data-a-target="chat-settings-balloon" style={{marginRight: '-5.3rem'}}>
|
||||
<div class="tw-border-radius-large tw-c-background-base tw-c-text-inherit tw-elevation-2">
|
||||
<div class="chat-settings__popover">
|
||||
<div class="chat-settings__header tw-align-items-center tw-c-background-base tw-flex tw-pd-x-1 tw-relative">
|
||||
|
|
8
src/utilities/data/category-fetch.gql
Normal file
8
src/utilities/data/category-fetch.gql
Normal file
|
@ -0,0 +1,8 @@
|
|||
query FFZ_GetGame($id: ID, $name: String) {
|
||||
game(name: $name, id: $id) {
|
||||
id
|
||||
name
|
||||
displayName
|
||||
boxArtURL(width: 40, height: 56)
|
||||
}
|
||||
}
|
|
@ -141,6 +141,15 @@ export default class TwitchData extends Module {
|
|||
};
|
||||
}
|
||||
|
||||
async getCategory(id, name) {
|
||||
const data = await this.queryApollo(
|
||||
require('./data/category-fetch.gql'),
|
||||
{ id, name }
|
||||
);
|
||||
|
||||
return get('data.game', data);
|
||||
}
|
||||
|
||||
|
||||
// ========================================================================
|
||||
// Users
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue