mirror of
https://github.com/FrankerFaceZ/FrankerFaceZ.git
synced 2025-06-27 12:55:55 +00:00
4.60.0
* Added: When searching in the FFZ Control Center, you can now use the tag `@modified` to filter by settings that have been changed in the current profile. * Added: Add-ons now have Changelog buttons that navigate to a changelog showing only entries for that add-on. * Changed: The Changelog pages now has nicer formatting for each commit, including add-on icons and clickable links to add-on sub-pages when viewing the Add-on Changelog. * API Added: To prevent FrankerFaceZ from loading into a page, include `disable_frankerfacez` in the URL query parameters. * Experiment Changed: Fix incorrect roll-out percentage for API-Based Link Lookups. This should be fully enabled. * Experiment Changed: Lower the percentage of users in the MQTT-Based PubSub experiment.
This commit is contained in:
parent
5046088bf7
commit
ba72969c51
17 changed files with 416 additions and 72 deletions
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "frankerfacez",
|
||||
"author": "Dan Salvato LLC",
|
||||
"version": "4.59.1",
|
||||
"version": "4.60.0",
|
||||
"description": "FrankerFaceZ is a Twitch enhancement suite.",
|
||||
"private": true,
|
||||
"license": "Apache-2.0",
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
import Module from 'utilities/module';
|
||||
import { EXTENSION, SERVER_OR_EXT } from 'utilities/constants';
|
||||
import { createElement } from 'utilities/dom';
|
||||
import { timeout, has } from 'utilities/object';
|
||||
import { timeout, has, deep_copy } from 'utilities/object';
|
||||
import { getBuster } from 'utilities/time';
|
||||
|
||||
const fetchJSON = (url, options) => fetch(url, options).then(r => r.ok ? r.json() : null).catch(() => null);
|
||||
|
@ -51,6 +51,7 @@ export default class AddonManager extends Module {
|
|||
|
||||
getExtraTerms: () => Object.values(this.addons).map(addon => addon.search_terms),
|
||||
|
||||
getFFZ: () => this,
|
||||
isReady: () => this.enabled,
|
||||
getAddons: () => Object.values(this.addons),
|
||||
hasAddon: id => this.hasAddon(id),
|
||||
|
@ -216,6 +217,16 @@ export default class AddonManager extends Module {
|
|||
this.addons[id] = [addon.id];
|
||||
}
|
||||
|
||||
if ( ! old )
|
||||
this.settings.addUI(`addon-changelog.${addon.id}`, {
|
||||
path: `Add-Ons > Changelog > ${addon.name}`,
|
||||
component: 'changelog',
|
||||
force_seen: true,
|
||||
addons: true,
|
||||
addon: deep_copy(addon),
|
||||
getFFZ: () => this
|
||||
});
|
||||
|
||||
this.emit(':added');
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,9 @@
|
|||
if ( /^(?:localhost\.rig|blog|im|chatdepot|tmi|api|brand|dev|gql|passport)\./.test(location.hostname) )
|
||||
return;
|
||||
|
||||
if ( /disable_frankerfacez/.test(location.search) )
|
||||
return;
|
||||
|
||||
const DEBUG = localStorage.ffzDebugMode == 'true' && document.body.classList.contains('ffz-dev'),
|
||||
HOST = location.hostname,
|
||||
SERVER = DEBUG ? '//localhost:8000' : '//cdn.frankerfacez.com',
|
||||
|
|
|
@ -5,6 +5,9 @@
|
|||
if ( /^(?:localhost\.rig|blog|im|chatdepot|tmi|api|brand|dev|gql|passport)\./.test(location.hostname) )
|
||||
return;
|
||||
|
||||
if ( /disable_frankerfacez/.test(location.search) )
|
||||
return;
|
||||
|
||||
const browser = globalThis.browser ?? globalThis.chrome,
|
||||
|
||||
HOST = location.hostname,
|
||||
|
|
|
@ -11,17 +11,17 @@
|
|||
"name": "API-Based Link Lookups",
|
||||
"description": "Use the new API to look up links instead of the socket cluster.",
|
||||
"groups": [
|
||||
{"value": true, "weight": 30},
|
||||
{"value": "cf", "weight": 10},
|
||||
{"value": false, "weight": 60}
|
||||
{"value": true, "weight": 0},
|
||||
{"value": "cf", "weight": 100},
|
||||
{"value": false, "weight": 0}
|
||||
]
|
||||
},
|
||||
"cf_pubsub": {
|
||||
"name": "MQTT-Based PubSub",
|
||||
"description": "An experimental new pubsub system that should be more reliable than the existing socket cluster.",
|
||||
"groups": [
|
||||
{"value": true, "weight": 10},
|
||||
{"value": false, "weight": 90}
|
||||
{"value": true, "weight": 5},
|
||||
{"value": false, "weight": 95}
|
||||
]
|
||||
}
|
||||
}
|
|
@ -60,6 +60,7 @@
|
|||
:item="item"
|
||||
:context="context"
|
||||
@navigate="navigate"
|
||||
@change-item="changeItem"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
@ -68,7 +69,7 @@
|
|||
visible: visible_addons.length,
|
||||
total: listed_addons.length
|
||||
}) }}
|
||||
<template v-if="filter && filter.length">
|
||||
<template v-if="filter">
|
||||
{{ t('addon.displaying.filtered', 'The visible add-ons are being filtered by your search. Clear it to view all available add-ons.') }}
|
||||
</template>
|
||||
</div>
|
||||
|
@ -222,10 +223,14 @@ export default {
|
|||
if ( this.filter_enabled && ! enabled )
|
||||
return false;
|
||||
|
||||
if ( ! this.filter || ! this.filter.length )
|
||||
return true;
|
||||
if ( this.filter ) {
|
||||
if ( this.filter.query ) {
|
||||
if ( ! addon.search_terms || ! addon.search_terms.includes(this.filter.query) )
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return addon.search_terms.includes(this.filter)
|
||||
return true;
|
||||
},
|
||||
|
||||
onAdded() {
|
||||
|
@ -242,6 +247,10 @@ export default {
|
|||
|
||||
navigate(...args) {
|
||||
this.$emit('navigate', ...args);
|
||||
},
|
||||
|
||||
changeItem(...args) {
|
||||
this.$emit('change-item', ...args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -145,6 +145,18 @@
|
|||
</span>
|
||||
</button>
|
||||
</template>
|
||||
<button
|
||||
v-if="! external"
|
||||
class="tw-button ffz-button--hollow tw-mg-r-1"
|
||||
@click="openChangelog"
|
||||
>
|
||||
<span class="tw-button__icon tw-button__icon--left">
|
||||
<figure class="ffz-i-doc-text" />
|
||||
</span>
|
||||
<span class="tw-button__text">
|
||||
{{ t('addon.changelog', 'Changelog') }}
|
||||
</span>
|
||||
</button>
|
||||
<a
|
||||
v-if="addon.website"
|
||||
:href="addon.website"
|
||||
|
@ -285,6 +297,16 @@ export default {
|
|||
this.$emit('navigate', ...list);
|
||||
},
|
||||
|
||||
openChangelog() {
|
||||
const list = [`add_ons.changelog.${this.id}`];
|
||||
if ( this.addon.short_name )
|
||||
list.push(`add_ons.changelog.${this.addon.short_name.toSnakeCase()}`);
|
||||
if ( this.addon.name )
|
||||
list.push(`add_ons.changelog.${this.addon.name.toSnakeCase()}`);
|
||||
|
||||
this.$emit('navigate', ...list);
|
||||
},
|
||||
|
||||
reloadAddon() {
|
||||
this.reloading = true;
|
||||
this.item.reloadAddon(this.id)
|
||||
|
|
|
@ -1,12 +1,26 @@
|
|||
<template lang="html">
|
||||
<div class="ffz--changelog tw-border-t tw-pd-t-1">
|
||||
<div class="tw-align-center">
|
||||
<h2 v-if="addons">
|
||||
<h3 v-if="addon" class="tw-mg-b-1 tw-flex tw-align-items-center tw-justify-content-center">
|
||||
<figure
|
||||
v-if="addon.icon"
|
||||
class="ffz-avatar ffz-avatar--size-30 tw-mg-r-05"
|
||||
>
|
||||
<img
|
||||
:src="addon.icon"
|
||||
class="tw-block tw-image tw-image-avatar"
|
||||
>
|
||||
</figure>
|
||||
{{ t('setting.add_ons.specific-changelog', '{name} Changelog', {
|
||||
name: addon.name
|
||||
}) }}
|
||||
</h3>
|
||||
<h3 v-else-if="addons" class="tw-mg-b-1">
|
||||
{{ t('setting.add_ons.changelog.title', 'Add-Ons Changelog') }}
|
||||
</h2>
|
||||
<h2 v-else>
|
||||
</h3>
|
||||
<h3 v-else class="tw-mg-b-1">
|
||||
{{ t('home.changelog', 'Changelog') }}
|
||||
</h2>
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<div v-if=" ! addons" class="tw-mg-b-1 tw-flex tw-align-items-center">
|
||||
|
@ -38,10 +52,30 @@
|
|||
<div v-if="! addons && commit.active" class="ffz-pill tw-mg-r-05">
|
||||
{{ t('home.changelog.current', 'Current Version') }}
|
||||
</div>
|
||||
<div v-if="commit.title" class="tw-font-size-4">
|
||||
<figure
|
||||
v-if="commit.icon"
|
||||
class="ffz-avatar ffz-avatar--size-20 tw-mg-r-05"
|
||||
>
|
||||
<img
|
||||
:src="commit.icon"
|
||||
class="tw-block tw-image tw-image-avatar"
|
||||
>
|
||||
</figure>
|
||||
<a
|
||||
v-if="commit.title && commit.title_nav"
|
||||
class="tw-font-size-5 ffz-link ffz-link--inherit"
|
||||
href="#"
|
||||
@click.prevent="titleNav(commit.title_nav)"
|
||||
>
|
||||
{{ commit.title }}
|
||||
</a>
|
||||
<div v-else-if="commit.title" class="tw-font-size-5">
|
||||
{{ commit.title }}
|
||||
</div>
|
||||
<div v-if="commit.author">
|
||||
<div v-if="commit.version" class="tw-font-size-4 tw-mg-l-05">
|
||||
<span class="tw-c-text-alt-2">v</span>{{ commit.version }}
|
||||
</div>
|
||||
<div v-if="commit.author" class="tw-mg-l-05">
|
||||
<t-list
|
||||
phrase="home.changelog.by-line"
|
||||
default="By: {user}"
|
||||
|
@ -86,6 +120,10 @@
|
|||
>({{ formatDate(commit.date) }})</time>
|
||||
</div>
|
||||
<markdown :source="commit.message" />
|
||||
<div v-for="entry in commit.segments" class="ffz--changelog-segment">
|
||||
<strong>{{ entry.key }}</strong>
|
||||
<markdown :source="entry.value" />
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
@ -111,7 +149,21 @@
|
|||
|
||||
import {get} from 'utilities/object';
|
||||
|
||||
const TITLE_MATCH = /^v?(\d+\.\d+\.\d+(?:-[^\n]+)?)\n+/;
|
||||
const TITLE_MATCH = /^(.+?)?\s*v?(\d+\.\d+\.\d+(?:\-[a-z0-9-]+)?)$/i,
|
||||
SETTING_REGEX = /\]\(~([^)]+)\)/g,
|
||||
CHANGE_REGEX = /^\*\s*([^:]+?):\s*(.+)$/i,
|
||||
ISSUE_REGEX = /(^|\s)#(\d+)\b/g;
|
||||
|
||||
|
||||
function linkify(text, repo) {
|
||||
text = text.replace(SETTING_REGEX, (_, link) => {
|
||||
return `](~${link})`
|
||||
});
|
||||
|
||||
return text.replace(ISSUE_REGEX, (_, space, number) => {
|
||||
return `${space}[#${number}](https://github.com/FrankerFaceZ/${repo}/issues/${number})`;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
export default {
|
||||
|
@ -120,6 +172,7 @@ export default {
|
|||
data() {
|
||||
return {
|
||||
error: false,
|
||||
addon: this.item.addon,
|
||||
addons: this.item.addons,
|
||||
nonversioned: false,
|
||||
loading: false,
|
||||
|
@ -130,35 +183,130 @@ export default {
|
|||
|
||||
computed: {
|
||||
display() {
|
||||
window.thing = this;
|
||||
|
||||
const out = [],
|
||||
addons = this.addons ? this.item.getFFZ().resolve('addons') : null,
|
||||
old_commit = this.t('home.changelog.nonversioned', 'Non-Versioned Commit');
|
||||
|
||||
for(const commit of this.commits) {
|
||||
let message = commit.commit.message,
|
||||
const input = commit.commit.message;
|
||||
let title = old_commit,
|
||||
title_nav = null,
|
||||
icon = null,
|
||||
version = null,
|
||||
author = null,
|
||||
title = old_commit;
|
||||
sections = {},
|
||||
description = [];
|
||||
|
||||
if ( this.addons ) {
|
||||
title = null;
|
||||
author = commit.author;
|
||||
if ( /\bskiplog\b/i.test(input) && ! this.nonversion )
|
||||
continue;
|
||||
|
||||
} else {
|
||||
const match = TITLE_MATCH.exec(message);
|
||||
|
||||
if ( match ) {
|
||||
title = match[1];
|
||||
message = message.slice(match[0].length);
|
||||
} else if ( ! this.nonversioned )
|
||||
continue;
|
||||
}
|
||||
const lines = input.split(/\r?\n/),
|
||||
first = lines.shift(),
|
||||
match = first ? TITLE_MATCH.exec(first) : null;
|
||||
|
||||
const date = new Date(commit.commit.author.date),
|
||||
active = commit.sha === window.FrankerFaceZ.version_info.commit;
|
||||
active = commit.sha === window.FrankerFaceZ.version_info.commit,
|
||||
has_content = lines.length && match;
|
||||
|
||||
if ( ! this.nonversion && ! has_content )
|
||||
continue;
|
||||
|
||||
let last_bit = null;
|
||||
|
||||
if ( match ) {
|
||||
title = match[1];
|
||||
version = match[2];
|
||||
}
|
||||
|
||||
if ( has_content )
|
||||
for(const line of lines) {
|
||||
const trimmed = line.trim();
|
||||
if ( ! trimmed.length ) {
|
||||
if ( ! last_bit && description.length )
|
||||
description.push(line);
|
||||
continue;
|
||||
}
|
||||
|
||||
const m = CHANGE_REGEX.exec(trimmed);
|
||||
if ( ! m ) {
|
||||
if ( ! last_bit )
|
||||
description.push(line);
|
||||
else
|
||||
last_bit.push(trimmed);
|
||||
|
||||
} else {
|
||||
const section = sections[m[1]] = sections[m[1]] || [];
|
||||
last_bit = [m[2]];
|
||||
section.push(last_bit);
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
lines.unshift(first);
|
||||
description = lines;
|
||||
}
|
||||
|
||||
let message = description.join('\n').trim();
|
||||
|
||||
const segments = [];
|
||||
|
||||
for(const [key, val] of Object.entries(sections)) {
|
||||
if ( ! val?.length )
|
||||
continue;
|
||||
|
||||
const bit = val.map(x => `* ${x.join(' ')}`).join('\n').trim();
|
||||
|
||||
segments.push({
|
||||
key,
|
||||
value: linkify(bit, this.addons ? 'add-ons' : 'frankerfacez')
|
||||
});
|
||||
}
|
||||
|
||||
if ( this.addons ) {
|
||||
author = commit.author;
|
||||
|
||||
if ( title ) {
|
||||
const ltitle = title.toLowerCase();
|
||||
|
||||
if ( addons?.addons )
|
||||
for(const addon of Object.values(addons.addons)) {
|
||||
if ((addon.short_name && addon.short_name.toLowerCase() === ltitle) ||
|
||||
(addon.name && addon.name.toLowerCase() === ltitle) ||
|
||||
(addon.id && addon.id.toLowerCase() === ltitle)
|
||||
) {
|
||||
icon = addon.icon;
|
||||
|
||||
title_nav = [`add_ons.changelog.${addon.id}`];
|
||||
if ( addon.short_name )
|
||||
title_nav.push(`add_ons.changelog.${addon.short_name.toSnakeCase()}`);
|
||||
if ( addon.name )
|
||||
title_nav.push(`add_ons.changelog.${addon.name.toSnakeCase()}`);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Default Icon
|
||||
if ( ! icon )
|
||||
icon = 'https://cdn.frankerfacez.com/badge/2/4/solid';
|
||||
}
|
||||
}
|
||||
|
||||
if ( this.addon ) {
|
||||
icon = null;
|
||||
title = null;
|
||||
}
|
||||
|
||||
out.push({
|
||||
icon,
|
||||
title,
|
||||
title_nav,
|
||||
version,
|
||||
author,
|
||||
message,
|
||||
segments,
|
||||
active,
|
||||
hash: commit.sha && commit.sha.slice(0,7),
|
||||
link: commit.html_url,
|
||||
|
@ -177,6 +325,11 @@ export default {
|
|||
},
|
||||
|
||||
methods: {
|
||||
titleNav(nav) {
|
||||
if ( Array.isArray(nav) )
|
||||
this.$emit('navigate', ...nav);
|
||||
},
|
||||
|
||||
formatDate(value) {
|
||||
if ( ! value )
|
||||
return '';
|
||||
|
@ -197,8 +350,15 @@ export default {
|
|||
|
||||
this.loading = true;
|
||||
|
||||
const url = new URL(`https://api.github.com/repos/frankerfacez/${this.addons ? 'add-ons' : 'frankerfacez'}/commits`);
|
||||
if ( until )
|
||||
url.searchParams.append('until', until);
|
||||
|
||||
if ( this.addon )
|
||||
url.searchParams.append('path', `src/${this.addon.id}`);
|
||||
|
||||
try {
|
||||
const resp = await fetch(`https://api.github.com/repos/frankerfacez/${this.addons ? 'add-ons' : 'frankerfacez'}/commits${until ? `?until=${until}` : ''}`),
|
||||
const resp = await fetch(url),
|
||||
data = resp.ok ? await resp.json() : null;
|
||||
|
||||
if ( ! data || ! Array.isArray(data) ) {
|
||||
|
|
|
@ -252,12 +252,15 @@ export default {
|
|||
},
|
||||
|
||||
visible_ffz() {
|
||||
const items = this.sorted_ffz,
|
||||
f = this.filter && this.filter.toLowerCase();
|
||||
if ( ! f )
|
||||
return items;
|
||||
let out = this.sorted_ffz;
|
||||
|
||||
return items.filter(x => matches(x, f));
|
||||
if ( this.filter?.query )
|
||||
out = out.filter(x => matches(x, this.filter.query));
|
||||
|
||||
if ( this.filter?.flags && this.filter.flags.has('modified') )
|
||||
out = out.filter(x => this.item.hasOverride(x.key));
|
||||
|
||||
return out;
|
||||
},
|
||||
|
||||
sorted_twitch() {
|
||||
|
@ -265,12 +268,15 @@ export default {
|
|||
},
|
||||
|
||||
visible_twitch() {
|
||||
const items = this.sorted_twitch,
|
||||
f = this.filter && this.filter.toLowerCase();
|
||||
if ( ! f )
|
||||
return items;
|
||||
let out = this.sorted_twitch;
|
||||
|
||||
return items.filter(x => matches(x, f));
|
||||
if ( this.filter?.query )
|
||||
out = out.filter(x => matches(x, this.filter.query));
|
||||
|
||||
if ( this.filter?.flags && this.filter.flags.has('modified') )
|
||||
out = out.filter(x => this.item.hasTwitchOverride(x.key));
|
||||
|
||||
return out;
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -79,6 +79,7 @@
|
|||
:current-item="currentItem"
|
||||
:modal="nav"
|
||||
:filter="filter"
|
||||
:context="context"
|
||||
@change-item="changeItem"
|
||||
@mark-seen="markSeen"
|
||||
@mark-expanded="markExpanded"
|
||||
|
@ -128,6 +129,10 @@
|
|||
import displace from 'displacejs';
|
||||
import { getDialogNextZ } from 'src/utilities/dialog';
|
||||
|
||||
const VALID_FLAGS = [
|
||||
'modified'
|
||||
];
|
||||
|
||||
export default {
|
||||
data() {
|
||||
const out = this.$vnode.data;
|
||||
|
@ -139,7 +144,32 @@ export default {
|
|||
|
||||
computed: {
|
||||
filter() {
|
||||
return this.query.toLowerCase()
|
||||
let query = this.query.toLowerCase();
|
||||
|
||||
let flags = new Set;
|
||||
query = query.replace(/(?<=^|\s)@(\S+)(?:\s+|$)/g, (match, flag, index) => {
|
||||
if ( VALID_FLAGS.includes(flag) ) {
|
||||
flags.add(flag);
|
||||
return '';
|
||||
}
|
||||
|
||||
return match;
|
||||
});
|
||||
|
||||
query = query.trim();
|
||||
if ( ! query.length )
|
||||
query = null;
|
||||
|
||||
if ( ! flags.size )
|
||||
flags = null;
|
||||
|
||||
if ( ! query && ! flags )
|
||||
return null;
|
||||
|
||||
return {
|
||||
flags,
|
||||
query
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -57,11 +57,32 @@ export default {
|
|||
this.$emit('navigate', ...args);
|
||||
},
|
||||
|
||||
shouldShow(item) {
|
||||
if ( ! this.filter || ! this.filter.length || ! item.search_terms )
|
||||
shouldShow(item, is_walking = false) {
|
||||
if ( ! this.filter || item.no_filter )
|
||||
return true;
|
||||
|
||||
return item.search_terms.includes(this.filter);
|
||||
if ( this.filter.flags ) {
|
||||
if ( this.filter.flags.has('modified') ) {
|
||||
// We need to tree walk for this one.
|
||||
if ( ! is_walking ) {
|
||||
for(const key of ['tabs', 'contents', 'items'])
|
||||
if ( item[key] )
|
||||
for(const thing of item[key])
|
||||
if ( this.shouldShow(thing) )
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( ! item.setting || ! this.context.currentProfile.has(item.setting) )
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ( this.filter.query ) {
|
||||
if ( ! item.search_terms || ! item.search_terms.includes(this.filter.query) )
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -149,15 +149,36 @@ export default {
|
|||
},
|
||||
|
||||
methods: {
|
||||
shouldShow(item) {
|
||||
if ( ! this.filter || ! this.filter.length || ! item.search_terms )
|
||||
shouldShow(item, is_walking = false) {
|
||||
if ( ! this.filter || item.no_filter )
|
||||
return true;
|
||||
|
||||
return item.no_filter || item.search_terms.includes(this.filter);
|
||||
if ( this.filter.flags ) {
|
||||
if ( this.filter.flags.has('modified') ) {
|
||||
// We need to tree walk for this one.
|
||||
if ( ! is_walking ) {
|
||||
for(const key of ['tabs', 'contents', 'items'])
|
||||
if ( item[key] )
|
||||
for(const thing of item[key])
|
||||
if ( this.shouldShow(thing) )
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( ! item.setting || ! this.context.currentProfile.has(item.setting) )
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ( this.filter.query ) {
|
||||
if ( ! item.search_terms || ! item.search_terms.includes(this.filter.query) )
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
countMatches(item, seen) {
|
||||
if ( ! this.filter || ! this.filter.length || ! item )
|
||||
if ( ! this.filter || ! item )
|
||||
return 0;
|
||||
|
||||
if ( seen && seen.has(item) )
|
||||
|
@ -175,7 +196,7 @@ export default {
|
|||
for(const thing of item[key])
|
||||
count += this.countMatches(thing, seen);
|
||||
|
||||
if ( item.setting && item.search_terms && item.search_terms.includes(this.filter) )
|
||||
if ( item.setting && this.shouldShow(item, true) )
|
||||
count++;
|
||||
|
||||
return count;
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
<li
|
||||
v-for="item in displayed"
|
||||
:key="item.full_key"
|
||||
:class="[currentItem === item ? 'active' : '']"
|
||||
:class="[(currentItem === item || item.hide_children && containsCurrent(item)) ? 'active' : '']"
|
||||
:data-key="item.full_key"
|
||||
role="presentation"
|
||||
>
|
||||
|
@ -26,7 +26,7 @@
|
|||
>
|
||||
<span
|
||||
:class="[
|
||||
item.items ? '' : 'ffz--invisible',
|
||||
(item.items && ! item.hide_children) ? '' : 'ffz--invisible',
|
||||
item.expanded ? 'ffz-i-down-dir' : 'ffz-i-right-dir'
|
||||
]"
|
||||
role="presentation"
|
||||
|
@ -46,10 +46,11 @@
|
|||
</span>
|
||||
</div>
|
||||
<menu-tree
|
||||
v-if="item.items && item.expanded"
|
||||
v-if="item.items && item.expanded && ! item.hide_children"
|
||||
:root="item"
|
||||
:current-item="currentItem"
|
||||
:modal="item.items"
|
||||
:context="context"
|
||||
:filter="filter"
|
||||
@change-item="i => $emit('change-item', i)"
|
||||
@mark-seen="i => $emit('mark-seen', i)"
|
||||
|
@ -97,7 +98,7 @@ function recursiveExpand(node, vue) {
|
|||
|
||||
|
||||
export default {
|
||||
props: ['root', 'modal', 'currentItem', 'filter'],
|
||||
props: ['root', 'modal', 'currentItem', 'filter', 'context'],
|
||||
|
||||
computed: {
|
||||
tabIndex() {
|
||||
|
@ -110,18 +111,36 @@ export default {
|
|||
},
|
||||
|
||||
methods: {
|
||||
shouldShow(item) {
|
||||
if ( ! this.filter || ! this.filter.length || this.containsCurrent(item) )
|
||||
shouldShow(item, is_walking = false) {
|
||||
if ( ! this.filter || this.containsCurrent(item) )
|
||||
return true;
|
||||
|
||||
if ( item.search_terms && item.search_terms.includes(this.filter) )
|
||||
return true;
|
||||
if ( this.filter.flags ) {
|
||||
if ( this.filter.flags.has('modified') ) {
|
||||
// We need to tree walk for this one.
|
||||
if ( ! is_walking ) {
|
||||
for(const key of ['tabs', 'contents', 'items'])
|
||||
if ( item[key] )
|
||||
for(const thing of item[key])
|
||||
if ( this.shouldShow(thing) )
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
if ( ! item.setting || ! this.context.currentProfile.has(item.setting) )
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ( this.filter.query ) {
|
||||
if ( ! item.search_terms || ! item.search_terms.includes(this.filter.query) )
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
countMatches(item, seen) {
|
||||
if ( ! this.filter || ! this.filter.length || ! item )
|
||||
if ( ! this.filter || ! item )
|
||||
return 0;
|
||||
|
||||
if ( seen && seen.has(item) )
|
||||
|
@ -139,7 +158,7 @@ export default {
|
|||
for(const thing of item[key])
|
||||
count += this.countMatches(thing, seen);
|
||||
|
||||
if ( item.setting && item.search_terms && item.search_terms.includes(this.filter) )
|
||||
if ( item.setting && this.shouldShow(item, true) )
|
||||
count++;
|
||||
|
||||
return count;
|
||||
|
|
|
@ -196,14 +196,16 @@ export default class MainMenu extends Module {
|
|||
|
||||
this.settings.addUI('changelog', {
|
||||
path: 'Home > Changelog @{"profile_warning": false}',
|
||||
component: 'changelog'
|
||||
component: 'changelog',
|
||||
getFFZ: () => this
|
||||
});
|
||||
|
||||
this.settings.addUI('addon-changelog', {
|
||||
path: 'Add-Ons > Changelog @{"sort": -1000, "profile_warning": false}',
|
||||
path: 'Add-Ons > Changelog @{"sort": -1000, "profile_warning": false, "hide_children": true}',
|
||||
component: 'changelog',
|
||||
force_seen: true,
|
||||
addons: true
|
||||
addons: true,
|
||||
getFFZ: () => this
|
||||
});
|
||||
|
||||
this.settings.addUI('legal', {
|
||||
|
|
|
@ -103,6 +103,16 @@
|
|||
background: linear-gradient(90deg, var(--color-background-body) 60%, transparent) !important;
|
||||
}
|
||||
|
||||
.directory-header-new__banner-cover {
|
||||
background: linear-gradient(0deg,
|
||||
var(--color-background-body),
|
||||
color-mix(in srgb, var(--color-background-body) 25%, transparent)
|
||||
), linear-gradient(90deg,
|
||||
var(--color-background-body),
|
||||
color-mix(in srgb, var(--color-background-body) 25%, transparent)
|
||||
);
|
||||
}
|
||||
|
||||
.emote-picker__emote-link {
|
||||
&:hover {
|
||||
background-color: var(--color-background-button-text-hover) !important;
|
||||
|
|
|
@ -119,7 +119,7 @@ export default {
|
|||
|
||||
methods: {
|
||||
countMatches(item, seen) {
|
||||
if ( ! this.filter || ! this.filter.length || ! item )
|
||||
if ( ! this.filter || ! item )
|
||||
return 0;
|
||||
|
||||
if ( seen && seen.has(item) )
|
||||
|
@ -137,7 +137,7 @@ export default {
|
|||
for(const thing of item[key])
|
||||
count += this.countMatches(thing, seen);
|
||||
|
||||
if ( item.setting && item.search_terms && item.search_terms.includes(this.filter) )
|
||||
if ( item.setting && this.shouldShow(item, true) )
|
||||
count++;
|
||||
|
||||
return count;
|
||||
|
@ -209,11 +209,32 @@ export default {
|
|||
}
|
||||
},
|
||||
|
||||
shouldShow(item) {
|
||||
if ( ! this.filter || ! this.filter.length || ! item.search_terms )
|
||||
shouldShow(item, is_walking = false) {
|
||||
if ( ! this.filter || item.no_filter )
|
||||
return true;
|
||||
|
||||
return item.search_terms.includes(this.filter);
|
||||
if ( this.filter.flags ) {
|
||||
if ( this.filter.flags.has('modified') ) {
|
||||
// We need to tree walk for this one.
|
||||
if ( ! is_walking ) {
|
||||
for(const key of ['tabs', 'contents', 'items'])
|
||||
if ( item[key] )
|
||||
for(const thing of item[key])
|
||||
if ( this.shouldShow(thing) )
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( ! item.setting || ! this.context.currentProfile.has(item.setting) )
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ( this.filter.query ) {
|
||||
if ( ! item.search_terms || ! item.search_terms.includes(this.filter.query) )
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -397,6 +397,12 @@ textarea.ffz-input {
|
|||
}
|
||||
}
|
||||
|
||||
.ffz--changelog-segment {
|
||||
ul {
|
||||
list-style: disc;
|
||||
}
|
||||
}
|
||||
|
||||
.ffz--clear-settings code,
|
||||
.ffz--experiments code {
|
||||
user-select: none;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue