mirror of
https://github.com/FrankerFaceZ/FrankerFaceZ.git
synced 2025-09-16 01:56:55 +00:00
4.20.72
* Added: Setting to change the height of chat actions. * Added: Profiles can now be toggled via hotkey. * Added: Profiles can now be imported from URL as well as File. * Added: Profiles with update URLs can have automatic updates disabled. * Fixed: Support for the `clips.twitch.tv` domain. * Fixed: Badges that make use of foreground text are no longer white in light themes. * Fixed: Mod Icons appearing smaller than normal. * Changed: Allow several types of actions, unrelated to moderation, to be used on a person's own chat messages. * Changed: Better warn users in the Control Center when the current profile is set to automatically update. * Changed: Make the FFZ Control Center remember which profile is selected when re-opening / refreshing. * API Added: Add-ons can now target specific supported flavors. Choices thus far are `main` and `clips`. * API Added: The `site.menu_button` now has `addToast(...)` and can display multiple toasts. Toasts can also time out. * API Fixed: `openFile(...)` never resolving if the user closes the dialog without selecting a file.
This commit is contained in:
parent
0d433c3ebd
commit
9086230686
61 changed files with 2267 additions and 222 deletions
|
@ -26,6 +26,23 @@ export default class Actions extends Module {
|
|||
this.actions = {};
|
||||
this.renderers = {};
|
||||
|
||||
this.settings.add('chat.actions.size', {
|
||||
default: 16,
|
||||
ui: {
|
||||
path: 'Chat > Actions @{"always_list_pages": true} >> Appearance',
|
||||
title: 'Action Size',
|
||||
description: "How tall actions should be, in pixels. This may be affected by your browser's zoom and font size settings.",
|
||||
component: 'setting-text-box',
|
||||
process(val) {
|
||||
val = parseInt(val, 10);
|
||||
if ( isNaN(val) || ! isFinite(val) || val <= 0 )
|
||||
return 16;
|
||||
|
||||
return val;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.settings.add('chat.actions.reasons', {
|
||||
default: [
|
||||
{v: {text: 'One-Man Spam', i18n: 'chat.reasons.spam'}},
|
||||
|
@ -444,7 +461,7 @@ export default class Actions extends Module {
|
|||
contents = def.render.call(this, ap, createElement, color);
|
||||
|
||||
actions.push(<button
|
||||
class={`ffz-tooltip tw-pd-x-05 ffz-mod-icon mod-icon tw-c-text-alt-2${disabled ? ' disabled' : ''}${has_color ? ' colored' : ''}`}
|
||||
class={`ffz-tooltip tw-pd-x-05 mod-icon ffz-mod-icon tw-c-text-alt-2${disabled ? ' disabled' : ''}${has_color ? ' colored' : ''}`}
|
||||
data-tooltip-type="action"
|
||||
data-action={data.action}
|
||||
data-options={data.options ? JSON.stringify(data.options) : null}
|
||||
|
@ -678,7 +695,7 @@ export default class Actions extends Module {
|
|||
|
||||
had_action = true;
|
||||
list.push(<button
|
||||
class={`ffz-tooltip ffz-mod-icon mod-icon tw-c-text-alt-2${disabled ? ' disabled' : ''}${has_color ? ' colored' : ''}${keys ? ` ffz-modifier-${keys}` : ''}${hover ? ' ffz-hover' : ''}`}
|
||||
class={`ffz-tooltip mod-icon ffz-mod-icon tw-c-text-alt-2${disabled ? ' disabled' : ''}${has_color ? ' colored' : ''}${keys ? ` ffz-modifier-${keys}` : ''}${hover ? ' ffz-hover' : ''}`}
|
||||
disabled={disabled}
|
||||
data-tooltip-type="action"
|
||||
data-action={data.action}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
'use strict';
|
||||
|
||||
import {createElement} from 'utilities/dom';
|
||||
import {durationForChat} from 'utilities/time';
|
||||
|
||||
|
||||
// ============================================================================
|
||||
|
@ -71,6 +70,8 @@ export const edit_overrides = {
|
|||
title: 'Change Name & Color',
|
||||
description: 'Allows you to set local overrides for a user\'s name and color in chat.',
|
||||
|
||||
can_self: true,
|
||||
|
||||
tooltip() {
|
||||
return this.i18n.t('chat.actions.edit_overrides', 'Change Name & Color')
|
||||
},
|
||||
|
@ -103,6 +104,8 @@ export const open_url = {
|
|||
title: 'Open URL',
|
||||
description: '{options.url}',
|
||||
|
||||
can_self: true,
|
||||
|
||||
tooltip(data) {
|
||||
const url = this.replaceVariables(data.options.url, data);
|
||||
|
||||
|
@ -150,6 +153,8 @@ export const chat = {
|
|||
title: 'Chat Command',
|
||||
description: '{options.command}',
|
||||
|
||||
can_self: true,
|
||||
|
||||
editor: () => import(/* webpackChunkName: 'main-menu' */ './components/edit-chat.vue'),
|
||||
|
||||
tooltip(data) {
|
||||
|
|
|
@ -93,7 +93,7 @@ export function generateOverrideCSS(data, style) {
|
|||
|
||||
export function generateBadgeCSS(badge, version, data, style, is_dark, badge_version = 2, color_fixer, fg_fixer, scale = 1, clickable = false) {
|
||||
let color = data.color || 'transparent',
|
||||
fore = data.fore || '#fff',
|
||||
fore = data.fore || is_dark ? '#fff' : '#000',
|
||||
base_image = data.image || (data.addon ? null : `${BASE_IMAGE}${badge_version}/${badge}${data.svg ? '.svg' : `/${version}/`}`),
|
||||
trans = false,
|
||||
invert = false,
|
||||
|
@ -176,7 +176,6 @@ export default class Badges extends Module {
|
|||
|
||||
this.inject('i18n');
|
||||
this.inject('settings');
|
||||
this.inject('socket');
|
||||
this.inject('tooltips');
|
||||
this.inject('experiments');
|
||||
|
||||
|
|
|
@ -61,7 +61,6 @@ export default class Emotes extends Module {
|
|||
|
||||
this.EmoteTypes = EmoteTypes;
|
||||
|
||||
this.inject('socket');
|
||||
this.inject('settings');
|
||||
this.inject('experiments');
|
||||
|
||||
|
@ -164,7 +163,7 @@ export default class Emotes extends Module {
|
|||
}
|
||||
}
|
||||
|
||||
this.socket.on(':command:follow_sets', this.updateFollowSets, this);
|
||||
this.on('socket:command:follow_sets', this.updateFollowSets, this);
|
||||
|
||||
this.loadGlobalSets();
|
||||
}
|
||||
|
|
|
@ -36,7 +36,6 @@ export default class Chat extends Module {
|
|||
this.inject('settings');
|
||||
this.inject('i18n');
|
||||
this.inject('tooltips');
|
||||
this.inject('socket');
|
||||
this.inject('experiments');
|
||||
|
||||
this.inject(Badges);
|
||||
|
@ -920,6 +919,8 @@ export default class Chat extends Module {
|
|||
|
||||
|
||||
onEnable() {
|
||||
this.socket = this.resolve('socket');
|
||||
|
||||
if ( this.context.get('chat.filtering.color-mentions') )
|
||||
this.createColorCache().then(() => this.emit(':update-lines'));
|
||||
|
||||
|
@ -1642,6 +1643,9 @@ export default class Chat extends Module {
|
|||
if ( provider == null )
|
||||
provider = this.experiments.getAssignment('api_links') ? 'test' : 'socket';
|
||||
|
||||
if ( provider === 'socket' && ! this.socket )
|
||||
provider = 'test';
|
||||
|
||||
if ( provider === 'socket' ) {
|
||||
timeout(this.socket.call('get_link', url), 15000)
|
||||
.then(data => handle(true, data))
|
||||
|
|
|
@ -77,7 +77,8 @@ export default class Room {
|
|||
if ( this.manager.rooms[this._login] === this )
|
||||
this.manager.rooms[this._login] = null;
|
||||
|
||||
this.manager.socket.unsubscribe(this, `room.${this.login}`);
|
||||
if ( this.manager.socket )
|
||||
this.manager.socket.unsubscribe(this, `room.${this.login}`);
|
||||
}
|
||||
|
||||
if ( this.manager.room_ids[this._id] === this )
|
||||
|
@ -158,7 +159,8 @@ export default class Room {
|
|||
const old_room = this.manager.rooms[this._login];
|
||||
if ( old_room === this ) {
|
||||
this.manager.rooms[this._login] = null;
|
||||
this.manager.socket.unsubscribe(this, `room.${this.login}`);
|
||||
if ( this.manager.socket )
|
||||
this.manager.socket.unsubscribe(this, `room.${this.login}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -173,7 +175,8 @@ export default class Room {
|
|||
// Make sure we didn't have a funky loop thing happen.
|
||||
this._login = val;
|
||||
this.manager.rooms[val] = this;
|
||||
this.manager.socket.subscribe(this, `room.${val}`);
|
||||
if ( this.manager.socket )
|
||||
this.manager.socket.subscribe(this, `room.${val}`);
|
||||
this.manager.emit(':room-update-login', this, val);
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
:data-tip="act.appearance.tooltip"
|
||||
:class="{'ffz-tooltip': tooltip, 'tw-pd-05': pad, 'colored': color && color.length > 0}"
|
||||
data-tooltip-type="action"
|
||||
class="ffz-mod-icon mod-icon tw-c-text-alt-2 tw-font-size-4"
|
||||
class="mod-icon ffz-mod-icon tw-c-text-alt-2 tw-font-size-4"
|
||||
>
|
||||
<component
|
||||
:is="renderer.component"
|
||||
|
|
|
@ -273,6 +273,11 @@ export default {
|
|||
if ( ! item )
|
||||
return;
|
||||
|
||||
if ( this.$refs.page && this.$refs.page.onBeforeChange ) {
|
||||
if ( this.$refs.page.onBeforeChange(this.currentItem, item) === false )
|
||||
return;
|
||||
}
|
||||
|
||||
this.changeItem(item);
|
||||
|
||||
// Asynchronously walk down the tab tree, so that
|
||||
|
|
|
@ -27,6 +27,19 @@
|
|||
</span>
|
||||
</div>
|
||||
</section>
|
||||
<section v-if="context.currentProfile.url && ! context.currentProfile.pause_updates && item.profile_warning !== false" class="tw-border-t tw-pd-t-1 tw-pd-b-2">
|
||||
<div class="tw-c-background-accent tw-c-text-overlay tw-pd-1">
|
||||
<h3 class="ffz-i-attention">
|
||||
{{ t('setting.profiles.updates', 'This profile will update automatically.') }}
|
||||
</h3>
|
||||
|
||||
<span>
|
||||
{{ t('setting.profile.updates-about',
|
||||
'This profile is set to automatically update. When it does, any changed settings within it will be reset. Profile Rules will be reset as well.')
|
||||
}}
|
||||
</span>
|
||||
</div>
|
||||
</section>
|
||||
<section v-if="context.has_update" class="tw-border-t tw-pd-t-1 tw-pd-b-2">
|
||||
<div class="tw-c-background-accent tw-c-text-overlay tw-pd-1">
|
||||
<h3 class="ffz-i-arrows-cw">
|
||||
|
@ -44,7 +57,7 @@
|
|||
>
|
||||
<markdown :source="t(item.desc_i18n_key || `${item.i18n_key}.description`, item.description)" />
|
||||
</section>
|
||||
<template v-if="! item.contents || ! item.contents.length">
|
||||
<template v-if="! item.contents || ! item.contents.length || item.always_list_pages">
|
||||
<ul class="tw-border-t tw-pd-y-1">
|
||||
<li
|
||||
v-for="i in item.items"
|
||||
|
|
|
@ -67,23 +67,6 @@
|
|||
</button>
|
||||
</div>
|
||||
|
||||
<div v-if="url" class="tw-c-background-accent-alt-2 tw-c-text-overlay tw-pd-1 tw-mg-b-1">
|
||||
<h5 class="ffz-i-download-cloud">
|
||||
{{ t('setting.profile.updates', 'This profile will update automatically from the following URL:') }}
|
||||
</h5>
|
||||
|
||||
<div>
|
||||
<a
|
||||
:href="url"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="tw-link tw-c-text-overlay"
|
||||
>
|
||||
{{ url }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ffz--menu-container tw-border-t">
|
||||
<header>
|
||||
{{ t('setting.data_management.profiles.edit.general', 'General') }}
|
||||
|
@ -114,13 +97,76 @@
|
|||
class="tw-full-width tw-border-radius-medium tw-font-size-6 tw-pd-x-1 tw-pd-y-05 ffz-input"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="ffz--widget">
|
||||
<div class="tw-flex tw-align-items-center">
|
||||
<label for="ffz:editor:hotkey">
|
||||
{{ t('setting.data_management.profiles.edit.hotkey', 'Hotkey') }}
|
||||
</label>
|
||||
|
||||
<key-picker
|
||||
id="ffz:editor:hotkey"
|
||||
ref="hotkey"
|
||||
v-model="hotkey"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<section class="tw-mg-t-05 tw-c-text-alt-2">
|
||||
<markdown :source="t('setting.data_management.profiles.hotkey.desc', 'Setting a hotkey allows you to toggle a profile on or off at any time by using the hotkey.\n\n**Note:** A profile that is toggled on may still be inactive due to its rules.')" />
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="url" class="ffz--menu-container tw-border-t">
|
||||
<header>
|
||||
<figure class="tw-inline tw-mg-r-05 ffz-i-download-cloud" />
|
||||
{{ t('setting.data_management.profiles.edit.updates', 'Automatic Updates') }}
|
||||
</header>
|
||||
|
||||
<section class="tw-pd-b-1 tw-c-text-alt-2">
|
||||
{{ t('setting.data_management.profiles.edit.updates.description',
|
||||
'This profile has an associated URL for automatic updates. When updates are enabled and the profile updates, all settings associated with the profile will be reset. The profile\'s rules will be reset as well. The Name, Description, and Hotkey will not reset.')
|
||||
}}
|
||||
</section>
|
||||
|
||||
<div class="ffz--widget tw-flex tw-flex-nowrap">
|
||||
<label for="ffz:editor:url">
|
||||
{{ t('setting.data_management.profiles.edit.url', 'Update URL') }}
|
||||
</label>
|
||||
|
||||
<input
|
||||
id="ffz:editor:url"
|
||||
readonly
|
||||
:value="url"
|
||||
class="tw-full-width tw-border-radius-medium tw-font-size-6 tw-pd-x-1 tw-pd-y-05 ffz-input"
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="ffz--widget ffz--checkbox">
|
||||
<div class="tw-flex tw-align-items-center ffz-checkbox">
|
||||
<input
|
||||
id="ffz:editor:update"
|
||||
ref="update"
|
||||
:checked="! pause"
|
||||
type="checkbox"
|
||||
class="ffz-checkbox__input"
|
||||
@change="onPauseChange"
|
||||
>
|
||||
|
||||
<label for="ffz:editor:update" class="ffz-checkbox__label">
|
||||
<span class="tw-mg-l-1">
|
||||
{{ t('setting.data_management.profiles.edit.update', 'Automatically update this profile.') }}
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ffz--menu-container tw-border-t">
|
||||
<header>
|
||||
{{ t('setting.data_management.profiles.edit.rules', 'Rules') }}
|
||||
</header>
|
||||
<section class="tw-pd-b-1">
|
||||
<section class="tw-pd-b-1 tw-c-text-alt-2">
|
||||
{{ t('setting.data_management.profiles.edit.rules.description',
|
||||
'Rules allows you to define a series of conditions under which this profile will be active. When there are multiple rules, they must all match for the profile to activate. Please use an `Or` rule to create a profile that activates by matching one of several rules.')
|
||||
}}
|
||||
|
@ -152,10 +198,14 @@ export default {
|
|||
old_name: null,
|
||||
old_desc: null,
|
||||
old_rules: null,
|
||||
old_hotkey: null,
|
||||
old_pause: null,
|
||||
|
||||
name: null,
|
||||
desc: null,
|
||||
hotkey: null,
|
||||
url: null,
|
||||
pause: null,
|
||||
unsaved: false,
|
||||
|
||||
rules: null,
|
||||
|
@ -184,6 +234,16 @@ export default {
|
|||
this.unsaved = true;
|
||||
},
|
||||
|
||||
hotkey() {
|
||||
if ( this.hotkey !== this.old_hotkey )
|
||||
this.unsaved = true;
|
||||
},
|
||||
|
||||
pause() {
|
||||
if ( this.pause !== this.old_pause )
|
||||
this.unsaved = true;
|
||||
},
|
||||
|
||||
rules: {
|
||||
handler() {
|
||||
if ( ! deep_equals(this.rules, this.old_rules) )
|
||||
|
@ -204,6 +264,10 @@ export default {
|
|||
},
|
||||
|
||||
methods: {
|
||||
onPauseChange() {
|
||||
this.pause = ! this.$refs.update.checked;
|
||||
},
|
||||
|
||||
resetExport() {
|
||||
this.export_error = false;
|
||||
this.export_error_message = null;
|
||||
|
@ -246,8 +310,10 @@ export default {
|
|||
profile.description :
|
||||
'';
|
||||
|
||||
this.old_hotkey = this.hotkey = profile ? profile.hotkey : null;
|
||||
this.old_rules = this.rules = profile ? deep_copy(profile.context) : [];
|
||||
this.url = profile ? profile.url : null;
|
||||
this.old_url = this.url = profile ? profile.url : null;
|
||||
this.old_pause = this.pause = profile ? profile.pause_updates : null;
|
||||
this.unsaved = ! profile;
|
||||
},
|
||||
|
||||
|
@ -272,14 +338,18 @@ export default {
|
|||
this.item.profile = this.context.createProfile({
|
||||
name: this.name,
|
||||
description: this.desc,
|
||||
context: this.rules
|
||||
context: this.rules,
|
||||
hotkey: this.hotkey,
|
||||
pause_updates: this.pause
|
||||
});
|
||||
|
||||
} else if ( this.unsaved ) {
|
||||
const changes = {
|
||||
name: this.name,
|
||||
description: this.desc,
|
||||
context: this.rules
|
||||
context: this.rules,
|
||||
hotkey: this.hotkey,
|
||||
pause_updates: this.pause
|
||||
};
|
||||
|
||||
// Disable i18n if required.
|
||||
|
|
|
@ -40,6 +40,8 @@
|
|||
{{ t('setting.profiles.drag', 'Drag profiles to change their priority.') }}
|
||||
</div>
|
||||
<button
|
||||
:class="{'tw-button--disabled': importing}"
|
||||
:disabled="importing"
|
||||
class="tw-mg-l-1 tw-button tw-button--text"
|
||||
@click="edit()"
|
||||
>
|
||||
|
@ -47,14 +49,73 @@
|
|||
{{ t('setting.profiles.new', 'New Profile') }}
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
class="tw-mg-l-1 tw-button tw-button--text"
|
||||
@click="doImport"
|
||||
<div
|
||||
v-on-clickaway="closeMenu"
|
||||
class="tw-relative"
|
||||
>
|
||||
<span class="tw-button__text ffz-i-upload">
|
||||
{{ t('setting.import', 'Import…') }}
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
:class="{'tw-button--disabled': importing}"
|
||||
:disabled="importing"
|
||||
class="tw-mg-l-1 tw-button tw-button--text"
|
||||
@click="toggleMenu"
|
||||
>
|
||||
<span class="tw-button__text ffz-i-upload">
|
||||
{{ t('setting.import', 'Import…') }}
|
||||
</span>
|
||||
<span class="tw-button__icon tw-button__icon--right">
|
||||
<figure class="ffz-i-down-dir" />
|
||||
</span>
|
||||
</button>
|
||||
<balloon
|
||||
v-if="menu_open"
|
||||
color="background-alt-2"
|
||||
dir="down-right"
|
||||
:size="menu_pasting ? 'md' : 'sm'"
|
||||
>
|
||||
<simplebar class="ffz-mh-30">
|
||||
<div v-if="menu_pasting" class="tw-pd-1">
|
||||
<div class="tw-flex tw-align-items-center">
|
||||
<input
|
||||
ref="paste"
|
||||
:placeholder="t('setting.paste-url.url', '[url]')"
|
||||
class="tw-flex-grow-1 tw-border-radius-medium tw-font-size-6 tw-pd-x-1 tw-pd-y-05 ffz-input"
|
||||
@keydown.enter="doImportURL"
|
||||
>
|
||||
<button
|
||||
class="tw-mg-l-05 tw-button"
|
||||
@click="doImportURL"
|
||||
>
|
||||
<span class="tw-button__text ffz-i-plus">
|
||||
{{ t('setting.import.do', 'Import') }}
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="tw-pd-y-1">
|
||||
<button
|
||||
class="ffz-interactable ffz-interactable--hover-enabled ffz-interactable--default tw-interactive tw-full-width"
|
||||
@click="preparePaste"
|
||||
>
|
||||
<div class="tw-flex tw-align-items-center tw-pd-y-05 tw-pd-x-1">
|
||||
<div class="tw-flex-grow-1 tw-mg-r-1 ffz-i-download-cloud">
|
||||
{{ t('setting.import.url', 'From URL') }}
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
<button
|
||||
class="ffz-interactable ffz-interactable--hover-enabled ffz-interactable--default tw-interactive tw-full-width"
|
||||
@click="doImport"
|
||||
>
|
||||
<div class="tw-flex tw-align-items-center tw-pd-y-05 tw-pd-x-1">
|
||||
<div class="tw-flex-grow-1 tw-mg-r-1 ffz-i-upload">
|
||||
{{ t('setting.import.file', 'From File') }}
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</simplebar>
|
||||
</balloon>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="import_error" class="tw-c-background-accent-alt-2 tw-c-text-overlay tw-pd-1 tw-mg-b-1 tw-flex tw-align-items-start">
|
||||
|
@ -82,6 +143,7 @@
|
|||
{{ import_message }}
|
||||
</section>
|
||||
<button
|
||||
v-if="import_closable"
|
||||
class="tw-button tw-button--text tw-relative tw-tooltip__container"
|
||||
@click="resetImport"
|
||||
>
|
||||
|
@ -173,9 +235,12 @@
|
|||
<span class="ffz-i-ellipsis-vert" />
|
||||
</div>
|
||||
|
||||
<div v-if="p.url" class="tw-flex tw-flex-shrink-0 tw-align-items-center tw-mg-r-1 tw-relative tw-tooltip__container tw-font-size-4">
|
||||
<span class="ffz-i-download-cloud" />
|
||||
<div class="tw-tooltip tw-tooltip--down tw-tooltip--align-left">
|
||||
<div
|
||||
v-if="p.url"
|
||||
class="tw-flex tw-flex-shrink-0 tw-align-items-center tw-mg-r-1 tw-relative tw-tooltip__container tw-font-size-4"
|
||||
>
|
||||
<span :class="`ffz-i-download-cloud${p.pause_updates ? ' ffz-unmatched-item' : ''}`" />
|
||||
<div v-if="! p.pause_updates" class="tw-tooltip tw-tooltip--down tw-tooltip--align-left">
|
||||
<div class="tw-mg-b-05">
|
||||
{{ t('setting.profile.updates', 'This profile will update automatically from the following URL:') }}
|
||||
</div>
|
||||
|
@ -241,7 +306,12 @@ export default {
|
|||
|
||||
data() {
|
||||
return {
|
||||
menu_open: false,
|
||||
menu_pasting: false,
|
||||
|
||||
importing: false,
|
||||
import_error: false,
|
||||
import_closable: true,
|
||||
import_error_message: null,
|
||||
import_message: null,
|
||||
import_profiles: null,
|
||||
|
@ -272,6 +342,24 @@ export default {
|
|||
},
|
||||
|
||||
methods: {
|
||||
closeMenu() {
|
||||
this.menu_open = false;
|
||||
this.menu_pasting = false;
|
||||
},
|
||||
|
||||
toggleMenu() {
|
||||
this.menu_open = ! this.menu_open;
|
||||
this.menu_pasting = false;
|
||||
},
|
||||
|
||||
preparePaste() {
|
||||
this.menu_open = true;
|
||||
this.menu_pasting = true;
|
||||
requestAnimationFrame(() => {
|
||||
this.$refs.paste.focus()
|
||||
});
|
||||
},
|
||||
|
||||
onProxyCheck() {
|
||||
const val = this.$refs.proxied.checked;
|
||||
this.context.setProxied(val);
|
||||
|
@ -304,7 +392,10 @@ export default {
|
|||
},
|
||||
|
||||
resetImport() {
|
||||
this.importing = false;
|
||||
this.import_url = null;
|
||||
this.import_error = false;
|
||||
this.import_closable = true;
|
||||
this.import_error_message = null;
|
||||
this.import_message = null;
|
||||
this.import_profiles = null;
|
||||
|
@ -313,12 +404,48 @@ export default {
|
|||
this.import_data = null;
|
||||
},
|
||||
|
||||
async doImport() {
|
||||
async doImportURL() {
|
||||
const url = this.$refs.paste.value;
|
||||
|
||||
this.closeMenu();
|
||||
this.resetImport();
|
||||
this.import_url = url;
|
||||
this.importing = true;
|
||||
this.import_closable = false;
|
||||
|
||||
this.import_message = this.t('setting.backup-restore.http-loading', 'Loading...');
|
||||
|
||||
let data;
|
||||
try {
|
||||
const response = await fetch(url);
|
||||
if ( ! response.ok )
|
||||
throw new Error;
|
||||
|
||||
data = await response.json();
|
||||
|
||||
} catch(err) {
|
||||
this.import_message = null;
|
||||
this.import_error = true;
|
||||
this.import_error_message = this.t('setting.backup-restore.http-error', 'Unable to read JSON from the provided URL.');
|
||||
return;
|
||||
}
|
||||
|
||||
this.import_message = null;
|
||||
this.importData(data);
|
||||
},
|
||||
|
||||
async doImport() {
|
||||
this.closeMenu();
|
||||
this.resetImport();
|
||||
this.importing = true;
|
||||
|
||||
let file, contents;
|
||||
try {
|
||||
file = await openFile('application/json,application/zip');
|
||||
if ( ! file ) {
|
||||
this.resetImport();
|
||||
return;
|
||||
}
|
||||
|
||||
// We might get a different MIME than expected, roll with it.
|
||||
if ( file.type.toLowerCase().includes('zip') ) {
|
||||
|
@ -345,6 +472,10 @@ export default {
|
|||
return;
|
||||
}
|
||||
|
||||
this.importData(data);
|
||||
},
|
||||
|
||||
importData(data) {
|
||||
if ( data && data.type === 'full' ) {
|
||||
let profiles = data.values && data.values.profiles;
|
||||
if ( profiles === undefined )
|
||||
|
@ -374,6 +505,9 @@ export default {
|
|||
},
|
||||
|
||||
importProfile(profile_data, data) {
|
||||
if ( this.import_url && ! profile_data.url )
|
||||
profile_data.url = this.import_url;
|
||||
|
||||
this.import_profile = profile_data;
|
||||
this.import_profile_data = data;
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
ref="button"
|
||||
:class="{active: opened}"
|
||||
tabindex="0"
|
||||
class="tw-c-background-alt tw-block tw-border tw-border-radius-medium tw-font-size-6 tw-full-width ffz-select tw-pd-l-1 tw-pd-r-3 tw-pd-y-05"
|
||||
class="tw-flex tw-align-items-center tw-border-radius-medium tw-font-size-6 tw-full-width ffz-select tw-pd-l-1 tw-pd-r-3 tw-pd-y-05"
|
||||
@keyup.up.stop.prevent="focusShow"
|
||||
@keyup.left.stop.prevent="focusShow"
|
||||
@keyup.down.stop.prevent="focusShow"
|
||||
|
@ -51,23 +51,32 @@
|
|||
@keyup.enter="changeProfile(p)"
|
||||
@click="changeProfile(p)"
|
||||
>
|
||||
<div
|
||||
v-if="! p.toggled"
|
||||
class="tw-tooltip__container ffz--profile-row__icon ffz-i-cancel tw-absolute"
|
||||
>
|
||||
<div class="tw-tooltip tw-tooltip--down tw-tooltip--align-right">
|
||||
{{ t('setting.profiles.disabled', 'This profile is disabled.') }}
|
||||
<div class="ffz--profile-row__icon-tray tw-flex">
|
||||
<div
|
||||
v-if="p.url"
|
||||
:class="`tw-tooltip__container ffz--profile-row__icon ffz-i-download-cloud tw-relative${p.pause_updates ? ' ffz-unmatched-item' : ''}`"
|
||||
>
|
||||
<div v-if="! p.pause_updates" class="tw-tooltip tw-tooltip--down tw-tooltip--align-right">
|
||||
{{ t('setting.profiles.updates', 'This profile will update automatically.') }}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="! p.toggled"
|
||||
class="tw-tooltip__container ffz--profile-row__icon ffz-i-cancel tw-relative"
|
||||
>
|
||||
<div class="tw-tooltip tw-tooltip--down tw-tooltip--align-right">
|
||||
{{ t('setting.profiles.disabled', 'This profile is disabled.') }}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="p.live"
|
||||
class="tw-tooltip__container ffz--profile-row__icon ffz-i-ok tw-relative"
|
||||
>
|
||||
<div class="tw-tooltip tw-tooltip--down tw-tooltip--align-right">
|
||||
{{ t('setting.profiles.active', 'This profile is enabled and active.') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="p.live"
|
||||
class="tw-tooltip__container ffz--profile-row__icon ffz-i-ok tw-absolute"
|
||||
>
|
||||
<div class="tw-tooltip tw-tooltip--down tw-tooltip--align-right">
|
||||
{{ t('setting.profiles.active', 'This profile is enabled and active.') }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<h4>{{ p.i18n_key ? t(p.i18n_key, p.title, p) : p.title }}</h4>
|
||||
<div v-if="p.description" class="description">
|
||||
|
@ -220,6 +229,17 @@ export default {
|
|||
|
||||
changeProfile(profile) {
|
||||
this.context.currentProfile = profile;
|
||||
|
||||
try {
|
||||
window.history.replaceState({
|
||||
...window.history.state,
|
||||
ffzccp: profile.id
|
||||
}, document.title);
|
||||
} catch(err) {
|
||||
/* no-op */
|
||||
console.error(err);
|
||||
}
|
||||
|
||||
this.focusHide();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
:open-up="openUp"
|
||||
:nullable="true"
|
||||
:value="color"
|
||||
class="tw-mg-05"
|
||||
@input="onInput"
|
||||
/>
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
:id="item.full_key"
|
||||
ref="control"
|
||||
:value="value"
|
||||
class="tw-mg-05"
|
||||
@input="onInput"
|
||||
/>
|
||||
|
||||
|
|
|
@ -686,13 +686,15 @@ export default class MainMenu extends Module {
|
|||
description: profile.description,
|
||||
desc_i18n_key: profile.desc_i18n_key || profile.i18n_key && `${profile.i18n_key}.description`,
|
||||
|
||||
hotkey: profile.hotkey,
|
||||
url: profile.url,
|
||||
pause_updates: profile.pause_updates,
|
||||
|
||||
move: idx => context.manager.moveProfile(profile.id, idx),
|
||||
save: () => profile.save(),
|
||||
update: data => {
|
||||
profile.data = deep_copy(data)
|
||||
profile.save()
|
||||
profile.data = deep_copy(data);
|
||||
profile.save();
|
||||
},
|
||||
|
||||
toggle: () => profile.toggled = ! profile.toggled,
|
||||
|
@ -717,9 +719,14 @@ export default class MainMenu extends Module {
|
|||
settings = this.settings,
|
||||
provider = settings.provider,
|
||||
context = this.context,
|
||||
[profiles, profile_keys] = this.getProfiles();
|
||||
[profiles, profile_keys] = this.getProfiles(),
|
||||
state = window.history.state,
|
||||
profile_id = state?.ffzccp;
|
||||
|
||||
let currentProfile = profile_keys[0];
|
||||
if ( profile_id != null )
|
||||
currentProfile = profile_keys[profile_id];
|
||||
|
||||
if ( ! currentProfile ) {
|
||||
for(let i=profiles.length - 1; i >= 0; i--) {
|
||||
if ( profiles[i].live ) {
|
||||
|
@ -735,7 +742,7 @@ export default class MainMenu extends Module {
|
|||
const _c = {
|
||||
profiles,
|
||||
profile_keys,
|
||||
currentProfile: profile_keys[0] || profiles[0],
|
||||
currentProfile,
|
||||
|
||||
exclusive: this.exclusive,
|
||||
can_proxy: context._context.can_proxy,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue