1
0
Fork 0
mirror of https://github.com/FrankerFaceZ/FrankerFaceZ.git synced 2025-07-01 16:48:32 +00:00

4.0.0-rc21

* Added: In-Line Chat Actions can now be set to display with specific modifier keys being held. This feature currently requires that Freeze Chat Scrolling is enabled. (Though, why you'd want to use this without that feature is beyond me.)
* Fixed: Bug with custom chat width in theater mode.
* Fixed: The `Get Bits` button appearing when disabled.
* Fixed: Issue with highlighting chat usernames when using Firefox.
* Fixed: Misc minor bugs.
This commit is contained in:
SirStendec 2019-05-16 14:46:26 -04:00
parent 63783472b7
commit bd11a6f2aa
16 changed files with 299 additions and 23 deletions

View file

@ -155,6 +155,9 @@ export class TranslationManager extends Module {
handleMessage(event) {
const msg = event.data;
if ( ! msg )
return;
if ( msg.type === 'seen' )
this.see(msg.key, true);

View file

@ -149,7 +149,7 @@ ${typeof x[1] === 'string' ? x[1] : JSON.stringify(x[1], null, 4)}`
FrankerFaceZ.Logger = Logger;
const VER = FrankerFaceZ.version_info = {
major: 4, minor: 0, revision: 0, extra: '-rc20.5',
major: 4, minor: 0, revision: 0, extra: '-rc21',
commit: __git_commit__,
build: __webpack_hash__,
toString: () =>

View file

@ -77,6 +77,7 @@ export default class Actions extends Module {
component: 'chat-actions',
context: ['user', 'room', 'message'],
inline: true,
modifiers: true,
data: () => {
const chat = this.resolve('site.chat');
@ -437,7 +438,10 @@ export default class Actions extends Module {
if ( current_level < 3 )
mod_icons = false;
const chat = this.resolve('site.chat');
const chat = this.resolve('site.chat'),
modified = [];
let had_action = false;
for(const data of this.parent.context.get('chat.actions.inline')) {
if ( ! data.action || ! data.appearance )
@ -445,6 +449,7 @@ export default class Actions extends Module {
const ap = data.appearance || {},
disp = data.display || {},
keys = disp.keys,
def = this.renderers[ap.type];
@ -459,8 +464,14 @@ export default class Actions extends Module {
color = has_color && (chat && chat.colors ? chat.colors.process(ap.color) : ap.color),
contents = def.render.call(this, ap, createElement, color);
actions.push(<button
class={`ffz-tooltip ffz-mod-icon mod-icon tw-c-text-alt-2${has_color ? ' colored' : ''}`}
let list = actions;
if ( keys )
list = modified;
had_action = true;
list.push(<button
class={`ffz-tooltip ffz-mod-icon mod-icon tw-c-text-alt-2${has_color ? ' colored' : ''}${keys ? ` ffz-modifier-${keys}` : ''}`}
data-tooltip-type="action"
data-action={data.action}
data-options={data.options ? JSON.stringify(data.options) : null}
@ -472,7 +483,7 @@ export default class Actions extends Module {
</button>);
}
if ( ! actions.length )
if ( ! had_action )
return null;
/*const room = current_room && JSON.stringify(current_room),
@ -483,12 +494,25 @@ export default class Actions extends Module {
type: msg.user.type
});*/
return (<div
let out = (<div
class="ffz--inline-actions ffz-action-data tw-inline-block tw-mg-r-05"
data-source="line"
>
{actions}
</div>);
if ( modified.length ) {
return [out,
(<div
class="ffz--inline-actions ffz--modifier-actions ffz-action-data"
data-source="line"
>
{modified}
</div>)
];
}
return out;
}

View file

@ -291,7 +291,7 @@ export const whisper = {
me = site && site.getUser(),
store = site && site.store;
if ( ! me || ! store || ! data.user.id || me.id == data.user.id )
if ( ! me || ! store || ! data.user || ! data.user.id || me.id == data.user.id )
return;
const id_1 = parseInt(me.id, 10),

View file

@ -935,7 +935,7 @@ export default class Chat extends Module {
for(const id in this.room_ids)
if ( has(this.room_ids, id) ) {
const room = this.room_ids[id];
if ( room ) {
if ( room && ! room.destroyed ) {
visited.add(room);
yield room;
}
@ -944,7 +944,7 @@ export default class Chat extends Module {
for(const login in this.rooms)
if ( has(this.rooms, login) ) {
const room = this.rooms[login];
if ( room && ! visited.has(room) )
if ( room && ! room.destroyed && ! visited.has(room) )
yield room;
}
}
@ -1347,6 +1347,7 @@ export default class Chat extends Module {
if ( type === 'text' )
res = e('span', {
className: 'text-fragment',
'data-a-target': 'chat-message-text'
}, token.text);

View file

@ -364,14 +364,20 @@ export default class Room {
// ========================================================================
ref(referrer) {
if ( ! this.refs )
throw new Error('Attempting to use destroyed Room');
clearTimeout(this._destroy_timer);
this._destroy_timer = null;
this.refs.add(referrer);
}
unref(referrer) {
if ( ! this.refs )
return;
this.refs.delete(referrer);
if ( ! this.users.size && ! this._destroy_timer )
if ( ! this.refs.size && ! this._destroy_timer )
this._destroy_timer = setTimeout(() => this.destroy(), 5000);
}

View file

@ -29,7 +29,7 @@ export const Links = {
render(token, createElement) {
return (<a
class="ffz-tooltip"
class="ffz-tooltip link-fragment"
data-tooltip-type="link"
data-url={token.url}
data-is-mail={token.is_mail}

View file

@ -119,6 +119,76 @@
<option :value="false">{{ t('setting.false', 'False') }}</option>
</select>
</div>
<div v-if="has_modifiers" class="tw-flex tw-align-items-start">
<label for="vis_modifiers">
{{ t('setting.actions.edit-visible.modifier', 'Modifiers') }}
</label>
<div>
<div class="ffz--inline tw-flex">
<div class="tw-pd-r-1 tw-checkbox">
<input
id="key_ctrl"
ref="key_ctrl"
:checked="edit_data.display.keys & 1"
type="checkbox"
class="tw-checkbox__input"
@change="onChangeKeys"
>
<label for="key_ctrl" class="tw-checkbox__label">
{{ t('setting.key.ctrl', 'Ctrl') }}
</label>
</div>
<div class="tw-pd-r-1 tw-checkbox">
<input
id="key_shift"
ref="key_shift"
:checked="edit_data.display.keys & 2"
type="checkbox"
class="tw-checkbox__input"
@change="onChangeKeys"
>
<label for="key_shift" class="tw-checkbox__label">
{{ t('setting.key.shift', 'Shift') }}
</label>
</div>
<div class="tw-pd-r-1 tw-checkbox">
<input
id="key_alt"
ref="key_alt"
:checked="edit_data.display.keys & 4"
type="checkbox"
class="tw-checkbox__input"
@change="onChangeKeys"
>
<label for="key_alt" class="tw-checkbox__label">
{{ t('setting.key.alt', 'Alt') }}
</label>
</div>
<div class="tw-pd-r-1 tw-checkbox">
<input
id="key_meta"
ref="key_meta"
:checked="edit_data.display.keys & 8"
type="checkbox"
class="tw-checkbox__input"
@change="onChangeKeys"
>
<label for="key_meta" class="tw-checkbox__label">
{{ t('setting.key.meta', 'Meta') }}
</label>
</div>
</div>
<div class="tw-pd-t-05">
Note: This currently requires Chat > Behavior > Freeze Chat Scrolling to be enabled.
</div>
</div>
</div>
</section>
<section class="tw-mg-t-1 tw-border-t tw-pd-t-1">
@ -215,7 +285,7 @@
import {has, maybe_call, deep_copy} from 'utilities/object';
export default {
props: ['action', 'data', 'inline', 'context'],
props: ['action', 'data', 'inline', 'context', 'modifiers'],
data() {
return {
@ -237,6 +307,10 @@ export default {
return this.context && this.context.includes('message')
},
has_modifiers() {
return this.modifiers
},
vars() {
const out = [],
ctx = this.context || [];
@ -373,6 +447,24 @@ export default {
else if ( disp.deleted === false )
out.push(this.t('setting.actions.visible.undeleted', 'if message not deleted'));
if ( disp.keys ) {
const key_out = [];
if ( disp.keys & 1 )
key_out.push(this.t('setting.key.ctrl', 'Ctrl'));
if ( disp.keys & 2 )
key_out.push(this.t('setting.key.shift', 'Shift'));
if ( disp.keys & 4 )
key_out.push(this.t('setting.key.alt', 'Alt'));
if ( disp.keys & 8 )
key_out.push(this.t('setting.key.meta', 'Meta'));
if ( key_out.length )
out.push(this.t('setting.actions.visible.modifier', 'when {key_list} {keys, plural, one {is} other {are}} held', {
key_list: key_out.join(' + '),
keys: key_out.length
}));
}
if ( ! out.length )
return this.t('setting.actions.visible.always', 'always');
@ -392,6 +484,23 @@ export default {
this.edit_data.options = val;
},
onChangeKeys() {
if ( ! this.editing )
return;
let i = 0;
if ( this.$refs.key_ctrl.checked )
i |= 1;
if ( this.$refs.key_shift.checked )
i |= 2;
if ( this.$refs.key_alt.checked )
i |= 4;
if ( this.$refs.key_meta.checked )
i |= 8;
this.edit_data.display.keys = i;
},
edit() {
if ( ! this.canEdit )
return;

View file

@ -193,6 +193,7 @@
:data="data"
:inline="item.inline"
:context="item.context"
:modifiers="item.modifiers"
@remove="remove(act)"
@save="save(act, $event)"
/>

View file

@ -38,7 +38,7 @@ export default class BitsButton extends Module {
this.BitsButton = this.fine.define(
'bits-button',
n => n.renderButton && n.toggleShowTutorial
n => n.toggleBalloon && n.toggleShowTutorial
);
}

View file

@ -214,6 +214,16 @@ export default class ChatHook extends Module {
}
});
this.settings.add('chat.use-width', {
requires: ['chat.width', 'context.ui.rightColumnExpanded'],
process(ctx) {
if ( ! ctx.get('context.ui.rightColumnExpanded') )
return false;
return ctx.get('chat.width') != 340;
}
});
this.settings.add('chat.bits.show-pinned', {
default: true,
ui: {
@ -349,6 +359,14 @@ export default class ChatHook extends Module {
updateChatCSS() {
if ( ! this._update_chat_css_timer )
this._update_chat_css_timer = setTimeout(() => this._updateChatCSS(), 0);
}
_updateChatCSS() {
clearTimeout(this._update_chat_css_timer);
this._update_chat_css_timer = null;
const width = this.chat.context.get('chat.width'),
size = this.chat.context.get('chat.font-size'),
emote_alignment = this.chat.context.get('chat.lines.emote-alignment'),
@ -364,7 +382,7 @@ export default class ChatHook extends Module {
this.css_tweaks.setVariable('chat-width', `${width/10}rem`);
this.css_tweaks.toggle('chat-font', size !== 12 || font);
this.css_tweaks.toggle('chat-width', width !== 340);
this.css_tweaks.toggle('chat-width', this.chat.context.get('chat.use-width'));
this.css_tweaks.toggle('emote-alignment-padded', emote_alignment === 1);
this.css_tweaks.toggle('emote-alignment-baseline', emote_alignment === 2);
@ -421,6 +439,7 @@ export default class ChatHook extends Module {
this.grabTypes();
this.chat.context.on('changed:chat.width', this.updateChatCSS, this);
this.chat.context.on('changed:chat.use-width', this.updateChatCSS, this);
this.chat.context.on('changed:chat.font-size', this.updateChatCSS, this);
this.chat.context.on('changed:chat.font-family', this.updateChatCSS, this);
this.chat.context.on('changed:chat.lines.emote-alignment', this.updateChatCSS, this);

View file

@ -156,8 +156,9 @@ export default class ChatLine extends Module {
e('span', {
className: 'chat-line__message--badges'
}, t.chat.badges.render(msg, e)),
e('button', {
e('span', {
className: 'chat-line__username notranslate',
role: 'button',
style: { color },
onClick: this.ffz_user_click_handler
}, [
@ -414,8 +415,9 @@ other {# messages were deleted by a moderator.}
e('span', {
className: 'chat-line__message--badges'
}, t.chat.badges.render(msg, e)),
e('button', {
e('span', {
className: 'chat-line__username notranslate',
role: 'button',
style: { color },
onClick: this.ffz_user_click_handler
}, [
@ -774,8 +776,9 @@ other {# messages were deleted by a moderator.}
e('span', {
className: 'chat-line__message--badges'
}, t.chat.badges.render(msg, e)),
e('button', {
e('span', {
className: 'chat-line__username notranslate',
role: 'button',
style: { color },
onClick: this.onExtensionNameClick
}, e('span', {

View file

@ -65,6 +65,19 @@ export default class Scroller extends Module {
});
}
updateUseKeys() {
const old_use = this.use_keys;
this.use_keys = false;
for(const act of this.chat.context.get('chat.actions.inline'))
if ( act && act.display && act.display.keys )
this.use_keys = true;
if ( this.use_keys !== old_use ) {
for(const inst of this.ChatScroller.instances)
inst && inst.ffzUpdateKeys && inst.ffzUpdateKeys();
}
}
onEnable() {
this.on('i18n:update', () => {
for(const inst of this.ChatScroller.instances)
@ -82,6 +95,9 @@ export default class Scroller extends Module {
}
});
this.chat.context.on('changed:chat.actions.inline', this.updateUseKeys, this);
this.updateUseKeys();
this.smoothScroll = this.chat.context.get('chat.scroller.smooth-scroll');
this.chat.context.on('changed:chat.scroller.smooth-scroll', val => {
this.smoothScroll = val;
@ -437,6 +453,8 @@ export default class Scroller extends Module {
this.ffz_ctrl = e.ctrlKey;
this.ffz_meta = e.metaKey;
this.ffzUpdateKeys();
if ( this.ffz_outside || t.freeze < 2 )
return;
@ -451,8 +469,43 @@ export default class Scroller extends Module {
}
cls.prototype.ffzUpdateKeys = function() {
if ( ! this._ffz_key_update )
this._ffz_key_update = requestAnimationFrame(() => this.ffz_updateKeys());
}
cls.prototype.ffz_updateKeys = function() {
cancelAnimationFrame(this._ffz_key_update);
this._ffz_key_update = null;
if ( ! t.use_keys && this.ffz_use_keys === t.use_keys )
return;
if ( ! this.scroll || ! this.scroll.root )
return;
this.ffz_use_keys = t.use_keys;
this.scroll.root.classList.toggle('ffz--keys', t.use_keys);
const ds = this.scroll.root.dataset;
if ( ! t.use_keys ) {
delete ds.alt;
delete ds.ctrl;
delete ds.shift;
delete ds.meta;
} else {
ds.alt = ! this.ffz_outside && this.ffz_alt;
ds.ctrl = ! this.ffz_outside && this.ffz_ctrl;
ds.shift = ! this.ffz_outside && this.ffz_shift;
ds.meta = ! this.ffz_outside && this.ffz_meta;
}
}
cls.prototype.ffzMouseMove = function(e) {
this.ffz_last_move = Date.now();
const was_outside = this.ffz_outside;
this.ffz_outside = false;
if ( this._ffz_outside ) {
clearTimeout(this._ffz_outside);
@ -465,8 +518,13 @@ export default class Scroller extends Module {
e.ctrlKey === this.ffz_ctrl &&
e.metaKey === this.ffz_meta &&
e.screenY === this.ffz_sy &&
e.screenX === this.ffz_sx)
e.screenX === this.ffz_sx) {
if ( was_outside )
this.ffzUpdateKeys();
return;
}
this.ffz_alt = e.altKey;
this.ffz_shift = e.shiftKey;
@ -475,6 +533,8 @@ export default class Scroller extends Module {
this.ffz_sy = e.screenY;
this.ffz_sx = e.screenX;
this.ffzUpdateKeys();
const should_freeze = this.ffzShouldBeFrozen(),
changed = should_freeze !== this.ffz_frozen;
@ -492,6 +552,7 @@ export default class Scroller extends Module {
clearTimeout(this._ffz_outside);
this._ffz_outside = setTimeout(() => this.ffzMaybeUnfreeze(), 64);
this.ffzUpdateKeys();
}

View file

@ -61,9 +61,7 @@ export default class Layout extends Module {
const ratio = size.width / size.height;
return ratio <= ctx.get('layout.portrait-threshold');
},
changed: val => {
this.updatePortraitMode();
}
changed: () => this.updatePortraitMode()
});
this.settings.add('layout.inject-portrait', {

View file

@ -120,7 +120,9 @@ export default class Dialog extends EventEmitter {
container.classList.toggle('ffz-has-dialog', visible);
if ( ! visible ) {
if ( this._element )
this._element.remove();
this.emit('hide');
if ( this.factory )
@ -154,7 +156,7 @@ export default class Dialog extends EventEmitter {
}
toggleSize(event) {
if ( ! this._visible || event && event.button !== 0 )
if ( ! this._visible || event && event.button !== 0 || ! this._element )
return;
this._maximized = !this._maximized;

View file

@ -53,6 +53,55 @@
color: #fff;
}
.chat-line__message {
position: relative;
}
.ffz--modifier-actions {
position: absolute;
top: 0;
left: 0;
padding: .5rem 0;
background: rgba(255,255,255,0.5);
.dark-theme &,
.tw-root--theme-dark & {
background: rgba(0,0,0,0.5);
}
.ffz-mod-icon {
margin: 0 .5rem;
display: none;
}
}
.ffz--keys {
&[data-ctrl="true"] .ffz-modifier-1,
&[data-shift="true"] .ffz-modifier-2,
&[data-shift="true"][data-ctrl="true"] .ffz-modifier-3,
&[data-alt="true"] .ffz-modifier-4,
&[data-alt="true"][data-ctrl="true"] .ffz-modifier-5,
&[data-alt="true"][data-shift="true"] .ffz-modifier-6,
&[data-alt="true"][data-ctrl="true"][data-shift="true"] .ffz-modifier-7,
&[data-meta="true"] .ffz-modifier-8,
&[data-meta="true"][data-ctrl="true"] .ffz-modifier-9,
&[data-meta="true"][data-shift="true"] .ffz-modifier-10,
&[data-meta="true"][data-shift="true"][data-ctrl="true"] .ffz-modifier-11,
&[data-meta="true"][data-alt="true"] .ffz-modifier-12,
&[data-meta="true"][data-alt="true"][data-ctrl="true"] .ffz-modifier-13,
&[data-meta="true"][data-alt="true"][data-shift="true"] .ffz-modifier-14,
&[data-meta="true"][data-alt="true"][data-ctrl="true"][data-shift="true"] .ffz-modifier-15 {
display: inline-flex;
}
}
.ffz--modifier-actions:empty {
display: none;
}
.ffz--favorite {
position: absolute;