Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG-nightly.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

**The changes listed here are not assigned to an official release**.

- Added a shortcut (Ctrl+E) to open the Emote Menu
- Added shortcuts (Up/Down Arrows) to switch between providers in the Emote Menu
- The input box in the Emote Menu is now focused automatically upon opening
- Search in the Emote Menu will now automatically open the nearest tab where matches are found
- Fixed a user card crash
- Fixed an issue with the EventAPI connection closing on the first initialization
- Fixed an issue that prevented new chatters from appearing in autocompletion
Expand Down
56 changes: 31 additions & 25 deletions src/app/emote-menu/EmoteMenu.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
<div class="seventv-emote-menu-providers">
<template v-for="(b, key) in visibleProviders">
<div
v-if="b"
v-if="b || key === activeProvider"
:key="key"
class="seventv-emote-menu-provider-icon"
:selected="key === activeProvider"
Expand All @@ -26,7 +26,12 @@
</template>
</div>
<div v-if="!isSearchInputEnabled" class="seventv-emote-menu-search">
<input ref="searchInputRef" v-model="ctx.filter" class="seventv-emote-menu-search-input" />
<input
ref="searchInputRef"
v-model="ctx.filter"
class="seventv-emote-menu-search-input"
autofocus
/>
<div class="search-icon">
<SearchIcon />
</div>
Expand Down Expand Up @@ -54,11 +59,10 @@
</template>

<script setup lang="ts">
import { onMounted, reactive, ref, watchEffect } from "vue";
import { onClickOutside, onKeyStroke, useKeyModifier } from "@vueuse/core";
import { reactive, ref, watchEffect } from "vue";
import { onClickOutside, onKeyStroke, useEventListener, useKeyModifier } from "@vueuse/core";
import { useStore } from "@/store/main";
import { useChannelContext } from "@/composable/channel/useChannelContext";
import { getModuleRef } from "@/composable/useModule";
import { useConfig } from "@/composable/useSettings";
import SearchIcon from "@/assets/svg/icons/SearchIcon.vue";
import StarIcon from "@/assets/svg/icons/StarIcon.vue";
Expand Down Expand Up @@ -105,28 +109,15 @@ const visibleProviders = reactive<Record<EmoteMenuTabName, boolean>>({
PLATFORM: true,
EMOJI: true,
});

const inputModule = getModuleRef<"TWITCH", "chat-input-controller">("chat-input-controller");

onMounted(() => {
if (!inputModule.value?.instance) return;

/*
inputModule.value.instance.addButton(
"emote-menu",
EmoteMenuButton,
{
onClick: () => (ctx.open = !ctx.open),
},
1,
);*/
});

watchEffect(() => {
if (!containerRef.value) return;

containerRef.value.style.setProperty("--width", props.width ?? "unset");
containerRef.value.style.setProperty("--seventv-emote-menu-scale", props.scale ?? "3rem");

if (searchInputRef.value) {
searchInputRef.value.focus();
}
});

// Shortcut (ctrl+e)
Expand All @@ -138,6 +129,19 @@ onKeyStroke("e", (ev) => {
ev.preventDefault();
});

// Up/Down Arrow iterates providers
useEventListener("keydown", (ev) => {
if (!["ArrowUp", "ArrowDown"].includes(ev.key)) return;

const cur = Object.keys(visibleProviders).indexOf(activeProvider.value ?? "7TV");
const next = ev.key === "ArrowUp" ? cur + 1 : cur - 1;
const nextProvider = Object.keys(visibleProviders)[next];

if (nextProvider) {
activeProvider.value = nextProvider as EmoteMenuTabName;
}
});

// Toggle the menu's visibility
function toggle() {
ctx.open = !ctx.open;
Expand All @@ -148,7 +152,9 @@ function toggle() {
function onProviderVisibilityChange(provider: EmoteMenuTabName, visible: boolean) {
visibleProviders[provider] = visible;
if (!visible && provider === activeProvider.value) {
activeProvider.value = (Object.entries(visibleProviders).find(([, v]) => v)?.[0] ?? "7TV") as SevenTV.Provider;
activeProvider.value = (Object.entries(visibleProviders)
.slice(1)
.find(([, v]) => v)?.[0] ?? "7TV") as SevenTV.Provider;
}
}

Expand Down Expand Up @@ -254,10 +260,10 @@ onClickOutside(containerRef, (ev) => {
padding-left: 3em;
color: currentcolor;
outline: none;
transition: outline 140ms;
transition: background-color 140ms;

&:focus {
outline: 1px solid var(--seventv-primary);
background-color: var(--seventv-background-shade-2);
}
}
}
Expand Down
8 changes: 7 additions & 1 deletion src/app/emote-menu/EmoteMenuSet.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
<template>
<div ref="containerEl" class="seventv-emote-set-container" :collapsed="collapsed" :ephemeral="ephemeral">
<div
v-if="ephemeral || emotes.length"
ref="containerEl"
class="seventv-emote-set-container"
:collapsed="collapsed"
:ephemeral="ephemeral"
>
<div class="seventv-set-header">
<div class="seventv-set-header-icon">
<img v-if="es.owner && es.owner.avatar_url" :src="es.owner.avatar_url" />
Expand Down
29 changes: 3 additions & 26 deletions src/app/emote-menu/EmoteMenuTab.vue
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ const sets = emotes.byProvider(props.provider as SevenTV.Provider) ?? reactive({
const store = useStore();
const cosmetics = useCosmetics(store.identity?.id ?? "");
const actor = useActor();
const visibleSets = reactive<Set<SevenTV.EmoteSet>>(new Set());
const visibleSets = reactive<Set<string>>(new Set());
const sortedSets = ref([] as SevenTV.EmoteSet[]);
const favorites = useConfig<Set<string>>("ui.emote_menu.favorites");
// const usage = useConfig<Map<string, number>>("ui.emote_menu.usage");
Expand All @@ -139,9 +139,6 @@ const promotionPersonalSet: SevenTV.EmoteSet = {
emotes: [],
};

// "Most Used" is commented out pending refactor
// Note the logic for favorites is also bad, though doesn't affect as many users

function updateFavorites() {
if (!favorites.value) return [];

Expand All @@ -150,16 +147,6 @@ function updateFavorites() {
.filter((ae) => !!ae) as SevenTV.ActiveEmote[];
}

// function updateUsage() {
// if (!shouldShowUsage.value) return [];
//
// return Array.from(usage.value.entries())
// .sort((a, b) => b[1] - a[1])
// .map((e) => emotes.find((ae) => ae.id === e[0]))
// .filter((ae) => !!ae)
// .slice(0, 50) as SevenTV.ActiveEmote[];
// }

if (props.provider === "FAVORITE") {
// create favorite set
const favSet = (sets["FAVORITE"] = reactive({
Expand All @@ -168,19 +155,9 @@ if (props.provider === "FAVORITE") {
emotes: updateFavorites(),
} as SevenTV.EmoteSet));

// const usageSet = (sets["USAGE"] = reactive({
// id: "USAGE",
// name: t("emote_menu.most_used_set"),
// emotes: updateUsage(),
// } as SevenTV.EmoteSet));

watch(favorites, () => {
favSet.emotes = updateFavorites();
});

// watch([usage, shouldShowUsage], () => {
// usageSet.emotes = updateUsage();
// });
}

// Select an Emote Set to jump-scroll to
Expand Down Expand Up @@ -218,9 +195,9 @@ function sortFn(a: SevenTV.EmoteSet, b: SevenTV.EmoteSet) {

function updateVisibility(es: SevenTV.EmoteSet, state: boolean): void {
if (state) {
visibleSets.add(es);
visibleSets.add(es.id);
} else {
visibleSets.delete(es);
visibleSets.delete(es.id);
}

emit("provider-visible", !!visibleSets.size);
Expand Down
10 changes: 0 additions & 10 deletions src/site/twitch.tv/modules/emote-menu/EmoteMenu.vue
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@

<script setup lang="ts">
import { nextTick, onMounted, onUnmounted, ref, watch, watchEffect } from "vue";
import { onKeyStroke, useKeyModifier } from "@vueuse/core";
import { log } from "@/common/Logger";
import { HookedInstance } from "@/common/ReactHooks";
import { defineFunctionHook, definePropertyHook, unsetPropertyHook } from "@/common/Reflection";
Expand Down Expand Up @@ -76,15 +75,6 @@ onMounted(() => {
);
});

// Shortcut (ctrl+e)
const isCtrl = useKeyModifier("Control", { initial: false });
onKeyStroke("e", (ev) => {
if (!isCtrl.value) return;

toggle();
ev.preventDefault();
});

// Toggle the menu's visibility
function toggle(native?: boolean) {
const t = props.instance.component;
Expand Down