From bde0c953ca20a772976fe90568aa92663dc5ad2f Mon Sep 17 00:00:00 2001 From: ChrisScreams <103491518+ChrisScr3ams@users.noreply.github.com> Date: Wed, 19 Jun 2024 20:37:39 +0200 Subject: [PATCH 01/66] fix: Fixed an issue where twitch emotes were larger after hover (#1047) --- CHANGELOG-nightly.md | 4 ++++ package.json | 2 +- src/common/Image.ts | 2 +- src/common/Transform.ts | 4 ---- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/CHANGELOG-nightly.md b/CHANGELOG-nightly.md index 69341cea7..93a4cf14a 100644 --- a/CHANGELOG-nightly.md +++ b/CHANGELOG-nightly.md @@ -1,3 +1,7 @@ +### 3.1.1.2000 + +- Fixed an issue where twitch emotes were displayed larger after hovering over them + ### 3.1.0.3000 - Fixed video player stats not showing diff --git a/package.json b/package.json index 44e5446e0..f8f51cf7f 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "Improve your viewing experience on Twitch & YouTube with new features, emotes, vanity and performance.", "private": true, "version": "3.1.1", - "dev_version": "1.0", + "dev_version": "2.0", "scripts": { "start": "NODE_ENV=dev yarn build:dev && NODE_ENV=dev vite --mode dev", "build:section:app": "vite build --config vite.config.mts", diff --git a/src/common/Image.ts b/src/common/Image.ts index 7f6861046..4798c2146 100644 --- a/src/common/Image.ts +++ b/src/common/Image.ts @@ -1,7 +1,7 @@ import { useUserAgent } from "@/composable/useUserAgent"; const layout = { - PLATFORM: [1, 2, 3], + PLATFORM: [1, 2, 4], "7TV": [1, 2, 3, 4], FFZ: [1, 2, 4], BTTV: [1, 2, 4], diff --git a/src/common/Transform.ts b/src/common/Transform.ts index 2b2e23ce5..081cf3d34 100644 --- a/src/common/Transform.ts +++ b/src/common/Transform.ts @@ -68,10 +68,6 @@ export function convertTwitchEmote( name: "2.0", format: "PNG", }, - { - name: "3.0", - format: "PNG", - }, { name: "4.0", format: "PNG", From 1a39453a5e6ddd6a2dfb91bce19973747e943145 Mon Sep 17 00:00:00 2001 From: pimothyxd <29018740+pimothyxd@users.noreply.github.com> Date: Thu, 20 Jun 2024 00:06:05 +0200 Subject: [PATCH 02/66] fix(twitch/vod-chat): vod chat double timestamp (#1050) --- CHANGELOG-nightly.md | 1 + src/site/twitch.tv/modules/chat-vod/ChatVod.vue | 2 ++ 2 files changed, 3 insertions(+) diff --git a/CHANGELOG-nightly.md b/CHANGELOG-nightly.md index 93a4cf14a..ff84abbb5 100644 --- a/CHANGELOG-nightly.md +++ b/CHANGELOG-nightly.md @@ -1,6 +1,7 @@ ### 3.1.1.2000 - Fixed an issue where twitch emotes were displayed larger after hovering over them +- Fixed an issue causing VOD messages to display two timestamps ### 3.1.0.3000 diff --git a/src/site/twitch.tv/modules/chat-vod/ChatVod.vue b/src/site/twitch.tv/modules/chat-vod/ChatVod.vue index 0ea919f9b..44f7f62e7 100644 --- a/src/site/twitch.tv/modules/chat-vod/ChatVod.vue +++ b/src/site/twitch.tv/modules/chat-vod/ChatVod.vue @@ -111,6 +111,8 @@ definePropertyHook(props.controller.component, "props", { } else { ctx.actor.roles.delete("MODERATOR"); } + + properties.showTimestamps = false; }, }); From bf25d3da01d83924790c71e0c1e2ce82de8f7950 Mon Sep 17 00:00:00 2001 From: ChrisScreams <103491518+ChrisScr3ams@users.noreply.github.com> Date: Thu, 20 Jun 2024 00:07:58 +0200 Subject: [PATCH 03/66] feat: Added option to hide 7TV badges in chat (#1048) --- CHANGELOG-nightly.md | 1 + src/app/chat/UserTag.vue | 3 ++- src/site/twitch.tv/modules/chat/ChatModule.vue | 6 ++++++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG-nightly.md b/CHANGELOG-nightly.md index ff84abbb5..cdc29e0d9 100644 --- a/CHANGELOG-nightly.md +++ b/CHANGELOG-nightly.md @@ -2,6 +2,7 @@ - Fixed an issue where twitch emotes were displayed larger after hovering over them - Fixed an issue causing VOD messages to display two timestamps +- Added option to hide 7TV badges from chat ### 3.1.0.3000 diff --git a/src/app/chat/UserTag.vue b/src/app/chat/UserTag.vue index 2d6195f9a..71b32d33f 100644 --- a/src/app/chat/UserTag.vue +++ b/src/app/chat/UserTag.vue @@ -79,6 +79,7 @@ const ctx = useChannelContext(); const properties = useChatProperties(ctx); const cosmetics = useCosmetics(props.user.id); const shouldRenderPaint = useConfig("vanity.nametag_paints"); +const shouldRender7tvBadges = useConfig("vanity.7tv_Badges"); const betterUserCardEnabled = useConfig("chat.user_card"); const twitchBadges = ref([]); const twitchBadgeSets = toRef(properties, "twitchBadgeSets"); @@ -133,7 +134,7 @@ const stop = watch( } paint.value = shouldRenderPaint.value && paints && paints.size ? paints.values().next().value : null; - activeBadges.value = [...badges.values()]; + activeBadges.value = shouldRender7tvBadges.value && badges && badges.size ? [...badges.values()] : []; }, { immediate: true }, ); diff --git a/src/site/twitch.tv/modules/chat/ChatModule.vue b/src/site/twitch.tv/modules/chat/ChatModule.vue index 2e801d183..febd6291d 100644 --- a/src/site/twitch.tv/modules/chat/ChatModule.vue +++ b/src/site/twitch.tv/modules/chat/ChatModule.vue @@ -478,5 +478,11 @@ export const config = [ hint: "Whether or not to display nametag paints", defaultValue: true, }), + declareConfig("vanity.7tv_Badges", "TOGGLE", { + path: ["Appearance", "Vanity"], + label: "7TV Badges", + hint: "Whether or not to display 7TV Badges", + defaultValue: true, + }), ]; From 033834556d602232fadfe0f1f8130e4a5d575f7d Mon Sep 17 00:00:00 2001 From: Excellify Date: Thu, 20 Jun 2024 00:16:10 +0200 Subject: [PATCH 04/66] Let 7TV mods edit any set --- src/composable/useActor.ts | 1 + src/composable/useSetMutation.ts | 3 ++- .../custom-commands/Commands/Dashboard.vue | 21 +++++++++++++++++++ 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/composable/useActor.ts b/src/composable/useActor.ts index a772f9caa..f5986dfb3 100644 --- a/src/composable/useActor.ts +++ b/src/composable/useActor.ts @@ -10,6 +10,7 @@ class ActorContext { user: SevenTV.User | null = null; sub: SubscriptionResponse | null = null; token = useConfig("app.7tv.token") as unknown as string; + editAnySet = false; platform: Platform | null = null; platformUserID: string | null = null; diff --git a/src/composable/useSetMutation.ts b/src/composable/useSetMutation.ts index 00900b9d3..5528232ca 100644 --- a/src/composable/useSetMutation.ts +++ b/src/composable/useSetMutation.ts @@ -48,7 +48,8 @@ export function useSetMutation(setID?: string): SetMutation { const canEditIfLoggedIn = computed(() => { if (!actor.user) return false; if (ctx.id == actor.platformUserID) return true; - return (ctx.user?.editors ?? []).some((e) => e.id == actor.user?.id); + if ((ctx.user?.editors ?? []).some((e) => e.id == actor.user?.id)) return true; + return actor.editAnySet; }); const canEditSet = computed(() => { diff --git a/src/site/twitch.tv/modules/custom-commands/Commands/Dashboard.vue b/src/site/twitch.tv/modules/custom-commands/Commands/Dashboard.vue index 072a71c80..f89a681ad 100644 --- a/src/site/twitch.tv/modules/custom-commands/Commands/Dashboard.vue +++ b/src/site/twitch.tv/modules/custom-commands/Commands/Dashboard.vue @@ -16,6 +16,8 @@ import { nextTick, onMounted, onUnmounted, ref } from "vue"; import { refAutoReset } from "@vueuse/core"; import { SEVENTV_EMOTE_ID, SEVENTV_EMOTE_LINK } from "@/common/Constant"; +import { SevenTVRoles } from "@/common/Roles"; +import { useActor } from "@/composable/useActor"; import { useSetMutation } from "@/composable/useSetMutation"; import EnableTray from "./components/EnableTray.vue"; import { useSettingsMenu } from "@/app/settings/Settings"; @@ -203,11 +205,29 @@ const commandAlias: Twitch.ChatCommand = { group: "7TV", }; +const actor = useActor(); +const perms = [SevenTVRoles.ADMIN, SevenTVRoles.MODERATOR] as string[]; + +const commandEditAnySet: Twitch.ChatCommand = { + name: "letmemamageemoteset", + description: " Lets you edit any 7TV emote set", + helpText: "", + permissionLevel: 0, + handler: () => { + actor.editAnySet = true; + }, + group: "7TV", + get hidden() { + return actor.editAnySet || mut.canEditSet || !actor.user?.roles?.some((v) => perms.includes(v)); + }, +}; + onMounted(() => { props.add(commandSearch); props.add(commandEnable); props.add(commandDisable); props.add(commandAlias); + props.add(commandEditAnySet); }); onUnmounted(() => { @@ -215,6 +235,7 @@ onUnmounted(() => { props.remove(commandEnable); props.remove(commandDisable); props.remove(commandAlias); + props.remove(commandEditAnySet); }); From 0865e60a216c1cdde7cb0cf7aaf137873da45a12 Mon Sep 17 00:00:00 2001 From: Excellify Date: Thu, 20 Jun 2024 00:33:12 +0200 Subject: [PATCH 05/66] Hide bits from community button Added ability to hide the bits on the community button --- .../twitch.tv/modules/chat/ChatModule.vue | 22 +++++++++++++++++++ src/types/twitch.d.ts | 4 ++++ 2 files changed, 26 insertions(+) diff --git a/src/site/twitch.tv/modules/chat/ChatModule.vue b/src/site/twitch.tv/modules/chat/ChatModule.vue index febd6291d..652d2af67 100644 --- a/src/site/twitch.tv/modules/chat/ChatModule.vue +++ b/src/site/twitch.tv/modules/chat/ChatModule.vue @@ -78,6 +78,22 @@ const chatEvents = useComponentHook({ predicate: (n) => n.onClearChatEvent, }); +const hideBitsBalance = useConfig("chat.hide_bits_balance"); +useComponentHook( + { + childSelector: ".community-points-summary", + predicate: (el) => el.shouldShowBitsBalance, + }, + { + functionHooks: { + shouldShowBitsBalance(this, old) { + if (hideBitsBalance.value) return false; + return old.call(this); + }, + }, + }, +); + const isHookable = ref(false); const isHookableDbc = refDebounced(isHookable, 200); @@ -484,5 +500,11 @@ export const config = [ hint: "Whether or not to display 7TV Badges", defaultValue: true, }), + declareConfig("chat.hide_bits_balance", "TOGGLE", { + label: "Hide Bits Balance", + hint: "Hide the bits balance in the chat.", + path: ["Chat", "Style"], + defaultValue: false, + }), ]; diff --git a/src/types/twitch.d.ts b/src/types/twitch.d.ts index 628179567..e31a94c85 100644 --- a/src/types/twitch.d.ts +++ b/src/types/twitch.d.ts @@ -437,6 +437,10 @@ declare module Twitch { determineGroup: (command: ChatCommand) => string; }; + export type ChatCommunityPointsButtonComponent = ReactExtended.WritableComponent<{}> & { + shouldShowBitsBalance: () => boolean; + }; + export type ChatChannelPointsClaimComponent = ReactExtended.WritableComponent<{ hidden: boolean; }> & { From 5223965fdd470c5fb779993f62519cd50123a815 Mon Sep 17 00:00:00 2001 From: Jarl Skajaa Gunnarsli Date: Thu, 20 Jun 2024 02:15:16 +0200 Subject: [PATCH 06/66] Clear active emotes on reset --- src/composable/chat/useChatEmotes.ts | 10 ++++++++-- src/site/twitch.tv/modules/chat/ChatController.vue | 2 +- .../twitch.tv/modules/chat/components/tray/ChatTray.ts | 2 +- .../twitch.tv/modules/chat/components/tray/Tray.vue | 1 + .../modules/custom-commands/Commands/Dashboard.vue | 1 + src/types/twitch.d.ts | 1 + 6 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/composable/chat/useChatEmotes.ts b/src/composable/chat/useChatEmotes.ts index 40c5838d2..3933da02e 100644 --- a/src/composable/chat/useChatEmotes.ts +++ b/src/composable/chat/useChatEmotes.ts @@ -32,7 +32,7 @@ export function useChatEmotes(ctx: ChannelContext) { m.set(ctx, x); } - function resetProviders() { + function reset() { if (!x) return; for (const k in x.providers) { @@ -42,6 +42,12 @@ export function useChatEmotes(ctx: ChannelContext) { delete x.providers[k as SevenTV.Provider][k2]; } } + + for (const k in x.active) { + const ae = x.active[k]; + if (ae.provider === "EMOJI") continue; + delete x.active[k]; + } } function byProvider(provider: SevenTV.Provider) { @@ -75,7 +81,7 @@ export function useChatEmotes(ctx: ChannelContext) { sets: x.sets, emojis: x.emojis, providers: x.providers, - resetProviders, + reset, byProvider, find, }); diff --git a/src/site/twitch.tv/modules/chat/ChatController.vue b/src/site/twitch.tv/modules/chat/ChatController.vue index 4fcaa488f..7431c9fc2 100644 --- a/src/site/twitch.tv/modules/chat/ChatController.vue +++ b/src/site/twitch.tv/modules/chat/ChatController.vue @@ -328,7 +328,7 @@ watch( messages.clear(); scroller.unpause(); - nextTick(() => emotes.resetProviders()); + nextTick(emotes.reset); }, { immediate: true }, ); diff --git a/src/site/twitch.tv/modules/chat/components/tray/ChatTray.ts b/src/site/twitch.tv/modules/chat/components/tray/ChatTray.ts index 15ba49569..dc8521eed 100644 --- a/src/site/twitch.tv/modules/chat/components/tray/ChatTray.ts +++ b/src/site/twitch.tv/modules/chat/components/tray/ChatTray.ts @@ -181,7 +181,7 @@ export function useTrayRef(options: Twitch.CustomTrayOptions, modifier: boolean $$typeof: REACT_ELEMENT_SYMBOL, key: "body", ref: (e: TrayRef) => (bodyRef.value = e), - type: "seventv-vue-component", + type: "seventv-tray-container-body", props: {}, }; diff --git a/src/site/twitch.tv/modules/chat/components/tray/Tray.vue b/src/site/twitch.tv/modules/chat/components/tray/Tray.vue index bbe9daca9..f0bbf3a7f 100644 --- a/src/site/twitch.tv/modules/chat/components/tray/Tray.vue +++ b/src/site/twitch.tv/modules/chat/components/tray/Tray.vue @@ -19,6 +19,7 @@ const props = withDefaults( messageHandler?: (v: string) => void; placeholder?: string; modifier?: boolean; + floating?: boolean; }>(), { modifier: false, diff --git a/src/site/twitch.tv/modules/custom-commands/Commands/Dashboard.vue b/src/site/twitch.tv/modules/custom-commands/Commands/Dashboard.vue index f89a681ad..bff5484fa 100644 --- a/src/site/twitch.tv/modules/custom-commands/Commands/Dashboard.vue +++ b/src/site/twitch.tv/modules/custom-commands/Commands/Dashboard.vue @@ -4,6 +4,7 @@ placeholder="Search again..." :input-value-override="search" disable-commands + floating :message-handler="(m) => (search = m)" > diff --git a/src/types/twitch.d.ts b/src/types/twitch.d.ts index e31a94c85..651add8ec 100644 --- a/src/types/twitch.d.ts +++ b/src/types/twitch.d.ts @@ -480,6 +480,7 @@ declare module Twitch { sendMessageHandler?: ChatTray.SendMessageHandler; messageHandler?: (v: string) => void; placeholder?: string; + floating?: boolean; } export namespace ChatTray { From 5dd7a6f1f25cf66bc23378645dc0967396ae347e Mon Sep 17 00:00:00 2001 From: Jarl Skajaa Gunnarsli Date: Thu, 20 Jun 2024 02:20:38 +0200 Subject: [PATCH 07/66] Update changelog --- CHANGELOG-nightly.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG-nightly.md b/CHANGELOG-nightly.md index cdc29e0d9..830b8aa91 100644 --- a/CHANGELOG-nightly.md +++ b/CHANGELOG-nightly.md @@ -1,8 +1,10 @@ ### 3.1.1.2000 +- Added option to hide 7TV badges from chat +- Added option to hide bits from community button - Fixed an issue where twitch emotes were displayed larger after hovering over them - Fixed an issue causing VOD messages to display two timestamps -- Added option to hide 7TV badges from chat +- Fixed an issue where the chat would keep rendering emotes of the previous channel ### 3.1.0.3000 From ff9a3a016fb05766be020f1f3b38f255d9c288ca Mon Sep 17 00:00:00 2001 From: Excellify Date: Thu, 20 Jun 2024 13:20:39 +0200 Subject: [PATCH 08/66] Setting wording --- src/site/twitch.tv/modules/chat/ChatModule.vue | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/site/twitch.tv/modules/chat/ChatModule.vue b/src/site/twitch.tv/modules/chat/ChatModule.vue index 652d2af67..96e2ab807 100644 --- a/src/site/twitch.tv/modules/chat/ChatModule.vue +++ b/src/site/twitch.tv/modules/chat/ChatModule.vue @@ -501,8 +501,8 @@ export const config = [ defaultValue: true, }), declareConfig("chat.hide_bits_balance", "TOGGLE", { - label: "Hide Bits Balance", - hint: "Hide the bits balance in the chat.", + label: "Hide Bits From Community Points Button", + hint: "Hide the bits balance from the community points button under the chatbox", path: ["Chat", "Style"], defaultValue: false, }), From b80054bbd15df0d2eb40ed0ba375b87059480b67 Mon Sep 17 00:00:00 2001 From: Excellify Date: Thu, 20 Jun 2024 14:48:00 +0200 Subject: [PATCH 09/66] Hook instance functions --- CHANGELOG-nightly.md | 4 ++++ src/common/ReactHooks.ts | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/CHANGELOG-nightly.md b/CHANGELOG-nightly.md index 830b8aa91..931e05fd7 100644 --- a/CHANGELOG-nightly.md +++ b/CHANGELOG-nightly.md @@ -1,3 +1,7 @@ +### 3.1.1.2100 + +- Fixed an issue where certain hooks would not work properly + ### 3.1.1.2000 - Added option to hide 7TV badges from chat diff --git a/src/common/ReactHooks.ts b/src/common/ReactHooks.ts index d42c04a65..259926d2f 100644 --- a/src/common/ReactHooks.ts +++ b/src/common/ReactHooks.ts @@ -306,6 +306,9 @@ export function defineComponentHook( if (options.functionHooks) { for (const [k, f] of Object.entries(options.functionHooks)) { defineFunctionHook(proto, k as keyof C, f); + for (const instance of instances) { + defineFunctionHook(instance, k as keyof C, f); + } } } @@ -413,6 +416,9 @@ export function useComponentHook( if (hook.cls) { unsetPropertyHook(hook.cls.prototype, k as keyof C); } + for (const instance of hook.instances) { + unsetPropertyHook(instance.component, k as keyof C); + } } } unhookComponent(hook); From 229d8cb8116e803c7a9bc9b8a3aba312ac5cf51e Mon Sep 17 00:00:00 2001 From: ChrisScreams <103491518+ChrisScr3ams@users.noreply.github.com> Date: Fri, 21 Jun 2024 15:32:06 +0200 Subject: [PATCH 10/66] Feat: Animated FFZ emote support (#1051) --- CHANGELOG-nightly.md | 4 ++++ package.json | 2 +- src/common/Transform.ts | 23 ++++++++++++++++------- src/types/app.d.ts | 1 + 4 files changed, 22 insertions(+), 8 deletions(-) diff --git a/CHANGELOG-nightly.md b/CHANGELOG-nightly.md index 931e05fd7..e0bce40f1 100644 --- a/CHANGELOG-nightly.md +++ b/CHANGELOG-nightly.md @@ -1,3 +1,7 @@ +### 3.1.1 3000 + +- Added support for animated FFZ emotes + ### 3.1.1.2100 - Fixed an issue where certain hooks would not work properly diff --git a/package.json b/package.json index f8f51cf7f..995eef39a 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "Improve your viewing experience on Twitch & YouTube with new features, emotes, vanity and performance.", "private": true, "version": "3.1.1", - "dev_version": "2.0", + "dev_version": "3.0", "scripts": { "start": "NODE_ENV=dev yarn build:dev && NODE_ENV=dev vite --mode dev", "build:section:app": "vite build --config vite.config.mts", diff --git a/src/common/Transform.ts b/src/common/Transform.ts index 081cf3d34..70bc37067 100644 --- a/src/common/Transform.ts +++ b/src/common/Transform.ts @@ -232,13 +232,22 @@ export function convertFFZEmote(data: FFZ.Emote): SevenTV.Emote { listed: true, owner: null, host: { - url: "//cdn.frankerfacez.com/emote/" + data.id, - files: Object.keys(data.urls).map((key) => { - return { - name: key, - format: "PNG", - }; - }), + url: data.animated + ? "//cdn.frankerfacez.com/emote/" + data.id + "/animated" + : "//cdn.frankerfacez.com/emote/" + data.id, + files: data.animated + ? Object.keys(data.animated).map((key) => { + return { + name: key, + format: "WEBP", + }; + }) + : Object.keys(data.urls).map((key) => { + return { + name: key, + format: "PNG", + }; + }), }, }; } diff --git a/src/types/app.d.ts b/src/types/app.d.ts index cf6894893..ab03e8062 100644 --- a/src/types/app.d.ts +++ b/src/types/app.d.ts @@ -480,6 +480,7 @@ declare namespace FFZ { display_name: string; } | null; urls: LinkMap; + animated?: LinkMap; } interface BadgesResponse { From f58bca9616a391fb2609f9c913029c313cb8088f Mon Sep 17 00:00:00 2001 From: Excellify Date: Fri, 21 Jun 2024 15:56:47 +0200 Subject: [PATCH 11/66] Added option to hide whispers --- CHANGELOG-nightly.md | 1 + .../hidden-elements/HiddenElementsModule.vue | 23 +++++++++++++++++++ .../modules/hidden-elements/hiddenElements.ts | 6 +++-- 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/CHANGELOG-nightly.md b/CHANGELOG-nightly.md index e0bce40f1..8f1ddee68 100644 --- a/CHANGELOG-nightly.md +++ b/CHANGELOG-nightly.md @@ -1,6 +1,7 @@ ### 3.1.1 3000 - Added support for animated FFZ emotes +- Added option to hide whispers ### 3.1.1.2100 diff --git a/src/site/twitch.tv/modules/hidden-elements/HiddenElementsModule.vue b/src/site/twitch.tv/modules/hidden-elements/HiddenElementsModule.vue index 8a1362e3a..c973ea261 100644 --- a/src/site/twitch.tv/modules/hidden-elements/HiddenElementsModule.vue +++ b/src/site/twitch.tv/modules/hidden-elements/HiddenElementsModule.vue @@ -155,6 +155,17 @@ export const config = [ hint: "If checked, on-screen celebrations will be hidden", defaultValue: false, }), + declareConfig("layout.hide_whispers", "DROPDOWN", { + path: ["Site Layout", "Twitch Features"], + label: "Hide Whispers", + hint: "Choose when to hide whispers, fullscreen means when chat is hidden", + options: [ + ["Never", 0], + ["Fullscreen", 1], + ["Allways", 2], + ], + defaultValue: 0, + }), ]; @@ -293,5 +304,17 @@ export const config = [ display: none !important; } } + +.seventv-hide-whispers-fullscreen { + div.whispers-open-threads:not(.whispers--right-column-expanded-beside) { + display: none !important; + } +} + +.seventv-hide-whispers-all { + div.whispers-open-threads { + display: none !important; + } +} /* stylelint-enable */ diff --git a/src/site/twitch.tv/modules/hidden-elements/hiddenElements.ts b/src/site/twitch.tv/modules/hidden-elements/hiddenElements.ts index 2b0ae8f3f..a220e40d6 100644 --- a/src/site/twitch.tv/modules/hidden-elements/hiddenElements.ts +++ b/src/site/twitch.tv/modules/hidden-elements/hiddenElements.ts @@ -1,4 +1,4 @@ -import { Ref } from "vue"; +import { Ref, computed } from "vue"; import { useConfig } from "@/composable/useSettings"; const hideLeaderboard = useConfig("layout.hide_channel_leaderboard"); @@ -22,7 +22,7 @@ const hideChatInputBox = useConfig("layout.hide_chat_input_box"); const hidePlayerExtensions = useConfig("player.hide_player_extensions"); const hideChannelPointBalanceButton = useConfig("layout.hide_channel_point_balance_button"); const hideOnscreenCelebrations = useConfig("player.hide_onscreen_celebrations"); - +const hideWhispers = useConfig("layout.hide_whispers"); export const hiddenElementSettings: Array<{ class: string; isHidden: Ref }> = [ { class: "seventv-hide-leaderboard", isHidden: hideLeaderboard }, { class: "seventv-hide-buttons-below-chatbox", isHidden: hideButtonsBelowChatbox }, @@ -45,4 +45,6 @@ export const hiddenElementSettings: Array<{ class: string; isHidden: Ref hideWhispers.value === 1) }, + { class: "seventv-hide-whispers-all", isHidden: computed(() => hideWhispers.value === 2) }, ]; From 40d8be532199b5d227e0935f8755a19d9b812fcb Mon Sep 17 00:00:00 2001 From: Excellify Date: Fri, 21 Jun 2024 16:43:20 +0200 Subject: [PATCH 12/66] Added tooltip to error messages in /search --- CHANGELOG-nightly.md | 1 + src/composable/useTooltip.ts | 2 +- .../Commands/components/EnableTray.vue | 19 ++++++++++--------- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/CHANGELOG-nightly.md b/CHANGELOG-nightly.md index 8f1ddee68..6d3775e0a 100644 --- a/CHANGELOG-nightly.md +++ b/CHANGELOG-nightly.md @@ -2,6 +2,7 @@ - Added support for animated FFZ emotes - Added option to hide whispers +- Added tooltip to error messages in /search ### 3.1.1.2100 diff --git a/src/composable/useTooltip.ts b/src/composable/useTooltip.ts index 47abc5f30..ad169af46 100644 --- a/src/composable/useTooltip.ts +++ b/src/composable/useTooltip.ts @@ -22,7 +22,7 @@ export function useTooltip(content?: string | Component, props?: Record <
>
- + {{ notice.type === "none" ? search : notice.message }} @@ -62,6 +62,7 @@ import Alias from "@/app/chat/EmoteAliasButton.vue"; import { useSettingsMenu } from "@/app/settings/Settings"; import UiScrollable from "@/ui/UiScrollable.vue"; import { useQuery } from "@vue/apollo-composable"; +import { GraphQLError } from "graphql"; const props = defineProps<{ search: string; @@ -78,7 +79,7 @@ const exactMatch = ref(false); const page = ref(1); const maxPage = computed(() => (result.value ? Math.ceil(result.value?.emotes.count / pageSize.value) : 1)); -type Notice = { type: "error" | "info" | "none"; message: string }; +type Notice = { type: "error" | "info" | "none"; message: string; tooltip?: string }; const notice = refAutoReset({ type: "none", message: "" }, 3000); const alias = ref(""); @@ -135,30 +136,30 @@ const onEmoteClick = (e: MouseEvent, emote: SevenTV.Emote) => { if (!props.mut.canEditSet) { navigator.clipboard.writeText(`https://7tv.app/emotes/${emote.id}`); - notice.value = { type: "info", message: "Copied" }; + notice.value = { type: "info", message: "Copied", tooltip: "Emote link copied to clipboard" }; return; } if (isEnabled(emote)) { - props.mut.remove(emote.id).catch(() => { - notice.value = { type: "error", message: "Error" }; + props.mut.remove(emote.id).catch((err: GraphQLError) => { + notice.value = { type: "error", message: "Error", tooltip: err.message }; }); return; } if (invalidAlias.value) { - notice.value = { type: "error", message: "Invalid alias" }; + notice.value = { type: "error", message: "Invalid alias", tooltip: "The alias contains illegal characters" }; return; } if (isConflict(emote)) { - notice.value = { type: "error", message: "Name conflict" }; + notice.value = { type: "error", message: "Name conflict", tooltip: "The emote name is already in use" }; return; } const name = alias.value !== "" ? alias.value : emote.name; - props.mut.add(emote.id, name).catch(() => { - notice.value = { type: "error", message: "Error" }; + props.mut.add(emote.id, name).catch((err: GraphQLError) => { + notice.value = { type: "error", message: "Error", tooltip: err.message }; }); alias.value = ""; }; From 82247764bf10e5dc373063b555c349ebbdb7c9a4 Mon Sep 17 00:00:00 2001 From: Excellify Date: Fri, 21 Jun 2024 17:10:30 +0200 Subject: [PATCH 13/66] Change env variables for dev --- .env.dev | 12 ++++++------ .old.env.dev | 7 +++++++ 2 files changed, 13 insertions(+), 6 deletions(-) create mode 100644 .old.env.dev diff --git a/.env.dev b/.env.dev index 5594f0fd0..5f9b7ff2e 100644 --- a/.env.dev +++ b/.env.dev @@ -1,7 +1,7 @@ NODE_ENV=development -VITE_APP_SITE="http://localhost:4200" -VITE_APP_API="http://localhost:3100/v3" -VITE_APP_API_GQL="http://localhost:3000/v3/gql" -VITE_APP_API_EVENTS="ws://localhost:3700/v3" -VITE_APP_API_EGVAULT="http://localhost:3444/v1" -VITE_APP_HOST="http://localhost:8080" +VITE_APP_SITE="https://7tv.app" +VITE_APP_API="https://7tv.io/v3" +VITE_APP_API_GQL="https://7tv.io/v3/gql" +VITE_APP_API_EVENTS="wss://events.7tv.io/v3" +VITE_APP_API_EGVAULT="https://egvault.7tv.io/v1" +VITE_APP_HOST="https://extension.7tv.gg" diff --git a/.old.env.dev b/.old.env.dev new file mode 100644 index 000000000..5594f0fd0 --- /dev/null +++ b/.old.env.dev @@ -0,0 +1,7 @@ +NODE_ENV=development +VITE_APP_SITE="http://localhost:4200" +VITE_APP_API="http://localhost:3100/v3" +VITE_APP_API_GQL="http://localhost:3000/v3/gql" +VITE_APP_API_EVENTS="ws://localhost:3700/v3" +VITE_APP_API_EGVAULT="http://localhost:3444/v1" +VITE_APP_HOST="http://localhost:8080" From 5263ce4b3b435a686ec8d6326bd8393e56a9630f Mon Sep 17 00:00:00 2001 From: FrantaBOT <68978333+FrantaBOT@users.noreply.github.com> Date: Sat, 3 Aug 2024 00:30:43 +0200 Subject: [PATCH 14/66] Removed scheme --- src/common/Image.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/Image.ts b/src/common/Image.ts index 7f6861046..3f990cf08 100644 --- a/src/common/Image.ts +++ b/src/common/Image.ts @@ -39,7 +39,7 @@ export function imageHostToSrcset( multiplier /= targetSize; if (srcset) srcset += ", "; - srcset += `https:${host.url}/${size.name} ${multiplier}x`; + srcset += `${host.url}/${size.name} ${multiplier}x`; } if (targetSize === 1) known[host.url] = srcset; From 1bfed194e318829180b7bf156822503ddf6a430c Mon Sep 17 00:00:00 2001 From: moath alayel <57598907+ftk789@users.noreply.github.com> Date: Tue, 17 Sep 2024 02:56:32 +0300 Subject: [PATCH 15/66] feat: initial implementation --- src/site/kick.com/KickSite.vue | 29 +--- src/site/kick.com/composable/useUserdata.ts | 24 ++++ .../kick.com/modules/chat/ChatController.vue | 14 +- .../kick.com/modules/chat/ChatMessage.vue | 8 +- src/site/kick.com/modules/chat/ChatModule.vue | 81 ++--------- .../kick.com/modules/chat/ChatObserver.vue | 134 ++++++++++++++++-- 6 files changed, 174 insertions(+), 116 deletions(-) create mode 100644 src/site/kick.com/composable/useUserdata.ts diff --git a/src/site/kick.com/KickSite.vue b/src/site/kick.com/KickSite.vue index 49961ac81..4007f5b1d 100644 --- a/src/site/kick.com/KickSite.vue +++ b/src/site/kick.com/KickSite.vue @@ -12,11 +12,12 @@ import { defineAsyncComponent, provide } from "vue"; import { useStore } from "@/store/main"; import { SITE_CURRENT_PLATFORM } from "@/common/Constant"; import { useActor } from "@/composable/useActor"; +import { useCookies } from "@/composable/useCookies"; import { getModule } from "@/composable/useModule"; import { useSettings } from "@/composable/useSettings"; import { useUserAgent } from "@/composable/useUserAgent"; import { useApp } from "./composable/useApp"; -import { usePinia } from "./composable/usePinia"; +import { useUserdata } from "./composable/useUserdata"; import { KickModuleID } from "@/types/kick.module"; const ModuleWrapper = defineAsyncComponent(() => import("@/site/global/ModuleWrapper.vue")); @@ -31,24 +32,11 @@ store.setPlatform("KICK", ["7TV"], []); document.body.setAttribute("seventv-kick", "true"); +const cookies = useCookies(); +const auth = cookies.get("XSRF-TOKEN"); + const app = useApp(); -const user = usePinia<{ - user: { - id: number; - username: string; - bio: string; - email: string; - streamer_channel: { - slug: string; - }; - discord?: string; - facebook?: string; - twitter?: string; - youtube?: string; - tiktok?: string; - instagram?: string; - }; -}>(app, "user"); +const user = await useUserdata(auth); if (user) { const updateIdentity = (data: typeof user.$state.user) => { @@ -71,10 +59,7 @@ if (user) { actor.setPlatformUserID("KICK", data.id.toString()); }; - user.$subscribe(() => { - updateIdentity(user.$state.user); - }); - updateIdentity(user.$state.user); + updateIdentity(user); } provide(SITE_CURRENT_PLATFORM, "KICK"); diff --git a/src/site/kick.com/composable/useUserdata.ts b/src/site/kick.com/composable/useUserdata.ts new file mode 100644 index 000000000..4c7feeede --- /dev/null +++ b/src/site/kick.com/composable/useUserdata.ts @@ -0,0 +1,24 @@ +export async function useUserdata(auth: string): Promise<{ + id: number; + username: string; + bio: string; + email: string; + streamer_channel: { + slug: string; + }; + discord?: string; + facebook?: string; + twitter?: string; + youtube?: string; + tiktok?: string; + instagram?: string; +}> { + const data = await fetch("https://kick.com/api/v1/user", { + headers: { + "X-XSRF-TOKEN": auth, + }, + method: "GET", + }); + + return await data.json(); +} diff --git a/src/site/kick.com/modules/chat/ChatController.vue b/src/site/kick.com/modules/chat/ChatController.vue index d63a00bdd..dfc89dc21 100644 --- a/src/site/kick.com/modules/chat/ChatController.vue +++ b/src/site/kick.com/modules/chat/ChatController.vue @@ -39,6 +39,7 @@ function onMessageSend(text: string) { const resp = await fetch(`https://kick.com/api/v2/channels/${props.slug}`).catch((err) => { log.error("failed to fetch channel data", err); }); +console.log(resp); if (!resp) throw new Error("failed to fetch channel data"); const { user_id: id } = await resp.json(); @@ -76,14 +77,15 @@ watchEffect(async () => { // Find chatroom element // "chatroom-top" is the heading - const chatroomTop = document.getElementById("chatroom-top"); - if (!chatroomTop || !chatroomTop.nextElementSibling) return; + const chatroomTop = document.getElementById("chatroom-messages"); + console.log(chatroomTop); + if (!chatroomTop) return; - chatList.value = chatroomTop.nextElementSibling as HTMLDivElement; + chatList.value = chatroomTop.firstElementChild as HTMLDivElement; // Create observer if (observer) observer.disconnect(); - if (!chatroomTop.nextElementSibling.firstElementChild) { + if (!chatroomTop.firstElementChild) { observer = new ObserverPromise( (records, emit) => { for (const rec of records) { @@ -93,7 +95,7 @@ watchEffect(async () => { emit(rec.target.firstElementChild as HTMLDivElement); } }, - chatroomTop.nextElementSibling, + chatroomTop, { childList: true, subtree: true, @@ -103,7 +105,7 @@ watchEffect(async () => { const el = await observer; shallowList.value = el; } else { - shallowList.value = chatroomTop.nextElementSibling.firstElementChild as HTMLDivElement; + shallowList.value = chatroomTop.firstElementChild as HTMLDivElement; } }); diff --git a/src/site/kick.com/modules/chat/ChatMessage.vue b/src/site/kick.com/modules/chat/ChatMessage.vue index 237c3738a..205070b22 100644 --- a/src/site/kick.com/modules/chat/ChatMessage.vue +++ b/src/site/kick.com/modules/chat/ChatMessage.vue @@ -78,19 +78,23 @@ useEventListener(props.bind.usernameEl.parentElement, "click", () => { emit("open-card", props.bind); }); +function KickEmoteSplitter(text: string): string[] { + return text.split(/\[emote:(\d+):?([a-zA-Z0-9-_!]*)?\]/g); +} + // Process kick's text entries into a containerized token function process(): void { if (props.parity) props.bind.el.setAttribute("parity", props.parity); props.bind.usernameEl.insertAdjacentElement("beforebegin", badgeContainer); - + console.log(cosmetics); if (cosmetics.paints.size) { updateElementStyles(props.bind.usernameEl, Array.from(cosmetics.paints.values())[0].id); } containers.value.length = 0; for (const el of props.bind.texts) { + //const message = props?.children?.props?.content ?? ""; const text = el.textContent ?? ""; - const newTokens = tokenize({ body: text, chatterMap: {}, diff --git a/src/site/kick.com/modules/chat/ChatModule.vue b/src/site/kick.com/modules/chat/ChatModule.vue index 1cb60f920..5cef436e8 100644 --- a/src/site/kick.com/modules/chat/ChatModule.vue +++ b/src/site/kick.com/modules/chat/ChatModule.vue @@ -1,9 +1,7 @@ diff --git a/src/site/kick.com/modules/chat/ChatObserver.vue b/src/site/kick.com/modules/chat/ChatObserver.vue index aaf722cac..4ca9bfcd9 100644 --- a/src/site/kick.com/modules/chat/ChatObserver.vue +++ b/src/site/kick.com/modules/chat/ChatObserver.vue @@ -35,30 +35,99 @@ const userCard = ref([]); const refreshRate = useConfig("chat.message_batch_duration", 100); -function patchMessageElement(el: HTMLDivElement, noBuffer?: boolean): void { - if (!el.hasAttribute("data-chat-entry")) return; // not a message +function getReactProps(element: HTMLElement): object | undefined { + for (const k in element) { + if (k.startsWith("__reactProps")) { + const props = Reflect.get(element, k); + + return props; + } + } + return undefined; +} + +interface ReplyReactMessageProps { + id: string; + metadata: { + original_sender: { + content: string; + id: string; + }; + original_sender: { + id: number; + username: string; + }; + }; + sender: { + id: number; + username: string; + slug: string; + }; +} + +interface DefaultReactMessageProps { + channelSlug: string; + message: { + id: string; + chatroom_id: number; + content: string; + created_at: string; + sender: { + id: number; + username: string; + slug: string; + type: string; + }; + }; + sender: { + id: number; + slug: string; + username: string; + }; + messageId: string; +} - const entryID = el.getAttribute("data-chat-entry")!; +type ReactMessageProps = DefaultReactMessageProps | ReplyReactMessageProps; + +function isDefaultReactMessageProps(props: unknown): props is DefaultReactMessageProps { + return ( + props != null && + typeof props === "object" && + "sender" in props && + typeof props.sender === "object" && + "message" in props && + typeof props.message === "object" + ); +} + +function getMessageReactProps(el: HTMLDivElement): KickReactMessageProps | undefined { + const messageElements = el.querySelector('div > div[style*="chatroom-font-size"]'); + if (!messageElements) return; + const props = getReactProps(messageElements); - const identity = el.querySelector(".chat-message-identity"); - if (!identity) return; // missing identity + if (!props || !Array.isArray(props.children)) return; - const entryUser = identity.querySelector(".chat-entry-username"); - if (!entryUser) return; // missing username + const child = props.children.find((child) => child?.props?.sender); + return child?.props; +} - const userID = entryUser.getAttribute("data-chat-entry-user-id"); - const username = entryUser.getAttribute("data-chat-entry-user"); - if (!userID || !username) return; // missing user ID or username +function patchMessageElement(el: HTMLDivElement, noBuffer?: boolean): void { + if (!el.hasAttribute("data-index")) return; // not a message + const props = getMessageReactProps(el); + console.log(props); + if (!props) return; - // find all untokenized content - const texts = el.querySelectorAll(".chat-entry-content"); + const entryID = isDefaultReactMessageProps(props) ? props.messageId : props.id; + const userID = props.sender.id.toString; + const username = props.sender.username; + const texts = el.querySelectorAll("span.font-normal"); const bind: ChatMessageBinding = { id: entryID, authorID: userID, authorName: username, texts: Array.from(texts), - usernameEl: entryUser, + usernameEl: el.querySelector("div.inline-flex > button"), el, }; @@ -66,6 +135,42 @@ function patchMessageElement(el: HTMLDivElement, noBuffer?: boolean): void { messageBuffer.value.push(bind); messageMap.set(el, bind); + console.log(bind); + + // const entryID = props.messageId; + // const userID = props.sender.id.toString(); + // const username = props.sender.username; + // const texts = el.querySelectorAll("span.font-normal"); + + // const entryID = el.getAttribute("data-index")!; + + // // const identity = el.querySelector(".chat-message-identity"); + // // if (!identity) return; // missing identity + + // const entryUser = el.firstElementChild.firstElementChild.querySelector("div.inline-flex > button"); + // if (!entryUser) return; // missing username + + // const userID = entryUser.getAttribute("title"); + // const username = entryUser.getAttribute("title"); + // if (!userID || !username) return; // missing user ID or username + + // // find all untokenized content + // const texts = el.firstElementChild.firstElementChild.querySelectorAll("span.font-normal"); + + // const bind: ChatMessageBinding = { + // id: entryID, + // authorID: userID, + // authorName: username, + // texts: Array.from(texts), + // usernameEl: entryUser, + // el, + // }; + + // if (!noBuffer) el.classList.add("seventv-chat-message-buffered"); + + // messageBuffer.value.push(bind); + // messageMap.set(el, bind); + // console.log(bind); } async function onOpenUserCard(bind: ChatMessageBinding) { @@ -97,7 +202,7 @@ async function onOpenUserCard(bind: ChatMessageBinding) { } function patch(): void { - const entries = props.listElement.querySelectorAll("[data-chat-entry]"); + const entries = props.listElement.querySelectorAll("[data-index]"); for (const el of Array.from(entries)) { patchMessageElement(el as HTMLDivElement, true); } @@ -157,6 +262,7 @@ watchEffect(() => { useMutationObserver( props.listElement, (records) => { + console.log(records); for (const rec of records) { rec.addedNodes.forEach((n) => { if (!(n instanceof HTMLDivElement)) return; From 9e0e479141af55167629174b513f931b5841d7ba Mon Sep 17 00:00:00 2001 From: moath alayel <57598907+ftk789@users.noreply.github.com> Date: Tue, 17 Sep 2024 03:03:04 +0300 Subject: [PATCH 16/66] Added useApp fix --- src/site/kick.com/composable/useApp.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/site/kick.com/composable/useApp.ts b/src/site/kick.com/composable/useApp.ts index 803f80c0f..3a16a8abf 100644 --- a/src/site/kick.com/composable/useApp.ts +++ b/src/site/kick.com/composable/useApp.ts @@ -5,7 +5,7 @@ const KICK_APP_KEY: InjectionKey> = Symbol("KICK_APP_KEY"); export function useApp() { let app = inject(KICK_APP_KEY, null); if (!app) { - app = (document.querySelector("#app") as unknown as Record)?.__vue_app__ as App; + app = document.querySelector("#channel-chatroom") as unknown as Record; if (!app) throw new Error("Could not acquire vue app"); inject(KICK_APP_KEY, app); From 5cb19399e3a9ddb52270ced545ebc1548a68a10a Mon Sep 17 00:00:00 2001 From: moath alayel <57598907+ftk789@users.noreply.github.com> Date: Wed, 18 Sep 2024 07:18:47 +0300 Subject: [PATCH 17/66] Parsing kick emotes and fix chat lag --- src/app/emote-menu/EmoteMenu.vue | 3 + .../kick.com/modules/chat/ChatController.vue | 2 - .../kick.com/modules/chat/ChatEmoteMenu.vue | 6 +- .../kick.com/modules/chat/ChatMessage.vue | 99 ++++++++++++++----- .../kick.com/modules/chat/ChatObserver.vue | 51 +++++----- 5 files changed, 104 insertions(+), 57 deletions(-) diff --git a/src/app/emote-menu/EmoteMenu.vue b/src/app/emote-menu/EmoteMenu.vue index 795e14270..681c16687 100644 --- a/src/app/emote-menu/EmoteMenu.vue +++ b/src/app/emote-menu/EmoteMenu.vue @@ -73,6 +73,8 @@ import { useSettingsMenu } from "@/app/settings/Settings"; import UiFloating from "@/ui/UiFloating.vue"; import { offset, shift } from "@floating-ui/dom"; +alert("vbyuh"); + export type EmoteMenuTabName = SevenTV.Provider | "FAVORITE"; const props = defineProps<{ @@ -151,6 +153,7 @@ useEventListener("keydown", (ev) => { // Toggle the menu's visibility function toggle() { + alert("buih"); ctx.open = !ctx.open; } diff --git a/src/site/kick.com/modules/chat/ChatController.vue b/src/site/kick.com/modules/chat/ChatController.vue index dfc89dc21..f7b8b39af 100644 --- a/src/site/kick.com/modules/chat/ChatController.vue +++ b/src/site/kick.com/modules/chat/ChatController.vue @@ -39,7 +39,6 @@ function onMessageSend(text: string) { const resp = await fetch(`https://kick.com/api/v2/channels/${props.slug}`).catch((err) => { log.error("failed to fetch channel data", err); }); -console.log(resp); if (!resp) throw new Error("failed to fetch channel data"); const { user_id: id } = await resp.json(); @@ -78,7 +77,6 @@ watchEffect(async () => { // Find chatroom element // "chatroom-top" is the heading const chatroomTop = document.getElementById("chatroom-messages"); - console.log(chatroomTop); if (!chatroomTop) return; chatList.value = chatroomTop.firstElementChild as HTMLDivElement; diff --git a/src/site/kick.com/modules/chat/ChatEmoteMenu.vue b/src/site/kick.com/modules/chat/ChatEmoteMenu.vue index c37d092c4..e5c250abf 100644 --- a/src/site/kick.com/modules/chat/ChatEmoteMenu.vue +++ b/src/site/kick.com/modules/chat/ChatEmoteMenu.vue @@ -39,13 +39,13 @@ function close(ev: MouseEvent): void { } watchEffect(() => { - const parent = document.getElementById("chatroom"); + const parent = document.getElementById("channel-chatroom"); if (!parent) return; - const inputRow = parent.querySelector(".chat-input-wrapper"); + const inputRow = document.getElementById("chat-input-wrapper"); if (!inputRow) return; - inputRow.lastElementChild?.insertAdjacentElement("beforebegin", emoteMenuButtonContainer); + //inputRow.lastElementChild?.insertAdjacentElement("beforebegin", emoteMenuButtonContainer); }); onUnmounted(() => { diff --git a/src/site/kick.com/modules/chat/ChatMessage.vue b/src/site/kick.com/modules/chat/ChatMessage.vue index 205070b22..1ccea2d9d 100644 --- a/src/site/kick.com/modules/chat/ChatMessage.vue +++ b/src/site/kick.com/modules/chat/ChatMessage.vue @@ -2,7 +2,18 @@ diff --git a/src/site/kick.com/modules/chat/ChatModule.vue b/src/site/kick.com/modules/chat/ChatModule.vue index 5cef436e8..f3e682244 100644 --- a/src/site/kick.com/modules/chat/ChatModule.vue +++ b/src/site/kick.com/modules/chat/ChatModule.vue @@ -5,14 +5,10 @@ From 842cc24e54330975bc1d80db8bf088cafe91fc1e Mon Sep 17 00:00:00 2001 From: moath alayel <57598907+ftk789@users.noreply.github.com> Date: Thu, 19 Sep 2024 00:05:14 +0300 Subject: [PATCH 23/66] Fixed URLs not being sent in chat correctly. --- .../kick.com/modules/chat/ChatMessage.vue | 24 +++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/src/site/kick.com/modules/chat/ChatMessage.vue b/src/site/kick.com/modules/chat/ChatMessage.vue index 1ccea2d9d..cc7686d14 100644 --- a/src/site/kick.com/modules/chat/ChatMessage.vue +++ b/src/site/kick.com/modules/chat/ChatMessage.vue @@ -6,7 +6,7 @@ + + {{ + token.content.url + }} + { }); function splitToken(token: string): string[] { - const parts = []; + const parts: string[] = []; let lastIndex = 0; - const regex = /\[emote:\d+:[^\]]+\]/g; + const regex = /\[emote:\d+:[^\]]+\]|https?:\/\/[^\s]+/g; let match; while ((match = regex.exec(token)) !== null) { if (lastIndex < match.index) { @@ -100,7 +105,9 @@ function extractTextWithKickEmotes(el: HTMLElement): string { if (node.nodeType === Node.TEXT_NODE) { result += node.textContent || ""; } else if (node instanceof HTMLElement) { - if (node.hasAttribute("data-emote-id") && node.hasAttribute("data-emote-name")) { + if (node.tagName === "A") { + result += node.innerText || ""; + } else if (node.hasAttribute("data-emote-id") && node.hasAttribute("data-emote-name")) { const emoteId = node.getAttribute("data-emote-id"); const emoteName = node.getAttribute("data-emote-name"); result += `[emote:${emoteId}:${emoteName}]`; @@ -113,7 +120,6 @@ function extractTextWithKickEmotes(el: HTMLElement): string { } function IsKickEmote(token: string): boolean { - // Check if the token matches the Kick emote format return token.startsWith("[emote:") && token.endsWith("]"); } @@ -130,7 +136,6 @@ function getKickEmoteUrl(token: string): string { function process(): void { cosmetics = useCosmetics(props.bind.authorID); if (cosmetics.paints.size) { - console.log("buh"); updateElementStyles(props.bind.usernameEl, Array.from(cosmetics.paints.values())[0].id); } containers.value.length = 0; @@ -218,11 +223,16 @@ onUnmounted(() => { word-break: break-word; } +.seventv-links { + text-decoration-line: underline; +} + .seventv-badge-list { display: inline-grid; vertical-align: middle; margin-right: 0.25rem; } + .kick-emote-token { width: 30px; height: 20px; From 6bb33290acd7eee1021fd88a04cf58061c11b37b Mon Sep 17 00:00:00 2001 From: moath alayel <57598907+ftk789@users.noreply.github.com> Date: Thu, 19 Sep 2024 01:19:50 +0300 Subject: [PATCH 24/66] URL parser fix to be visible in chat --- .../kick.com/modules/chat/ChatMessage.vue | 7 +- .../kick.com/modules/chat/ChatObserver.vue | 78 +++---------------- src/types/kick.module.d.ts | 26 +++++++ 3 files changed, 40 insertions(+), 71 deletions(-) diff --git a/src/site/kick.com/modules/chat/ChatMessage.vue b/src/site/kick.com/modules/chat/ChatMessage.vue index cc7686d14..b86c27204 100644 --- a/src/site/kick.com/modules/chat/ChatMessage.vue +++ b/src/site/kick.com/modules/chat/ChatMessage.vue @@ -3,7 +3,7 @@ diff --git a/src/site/kick.com/modules/chat-input/ChatInputModule.vue b/src/site/kick.com/modules/chat-input/ChatInputModule.vue new file mode 100644 index 000000000..2977fb71d --- /dev/null +++ b/src/site/kick.com/modules/chat-input/ChatInputModule.vue @@ -0,0 +1,97 @@ + + + diff --git a/src/site/kick.com/modules/emote-menu/EmoteMenu.vue b/src/site/kick.com/modules/emote-menu/EmoteMenu.vue new file mode 100644 index 000000000..605272a01 --- /dev/null +++ b/src/site/kick.com/modules/emote-menu/EmoteMenu.vue @@ -0,0 +1,100 @@ + + + + + diff --git a/src/site/kick.com/modules/emote-menu/EmoteMenuModule.vue b/src/site/kick.com/modules/emote-menu/EmoteMenuModule.vue new file mode 100644 index 000000000..de6c78127 --- /dev/null +++ b/src/site/kick.com/modules/emote-menu/EmoteMenuModule.vue @@ -0,0 +1,54 @@ + + + diff --git a/src/site/twitch.tv/modules/chat-input/ChatInput.vue b/src/site/twitch.tv/modules/chat-input/ChatInput.vue index 8cdfda613..b07fd2f09 100644 --- a/src/site/twitch.tv/modules/chat-input/ChatInput.vue +++ b/src/site/twitch.tv/modules/chat-input/ChatInput.vue @@ -17,6 +17,7 @@ import { useMagicKeys } from "@vueuse/core"; import { useStore } from "@/store/main"; import { REACT_TYPEOF_TOKEN } from "@/common/Constant"; import { imageHostToSrcset } from "@/common/Image"; +import { TabToken, getSearchRange } from "@/common/Input"; import { HookedInstance } from "@/common/ReactHooks"; import { defineFunctionHook, @@ -34,12 +35,6 @@ import { useConfig } from "@/composable/useSettings"; import { useUserAgent } from "@/composable/useUserAgent"; import ChatInputCarousel from "./ChatInputCarousel.vue"; -export interface TabToken { - token: string; - priority: number; - item?: SevenTV.ActiveEmote; -} - const props = defineProps<{ instance: HookedInstance; }>(); @@ -195,19 +190,7 @@ function handleTabPress(ev: KeyboardEvent | null, isBackwards?: boolean): void { const searchText = currentNode.text; const searchStart = cursorLocation.offset; - for (let i = searchStart; ; i--) { - if (i < 1 || (searchText.charAt(i - 1) === " " && i !== searchStart)) { - wordStart = i; - break; - } - } - - for (let i = searchStart + 1; ; i++) { - if (i > searchText.length || searchText.charAt(i - 1) === " ") { - wordEnd = i - 1; - break; - } - } + [wordStart, wordEnd] = getSearchRange(searchText, searchStart); if (cursorLocation.offset != wordStart) { currentWord = searchText.substring(wordStart, wordEnd); diff --git a/src/site/twitch.tv/modules/chat-input/ChatInputCarousel.vue b/src/site/twitch.tv/modules/chat-input/ChatInputCarousel.vue index dab7d0c25..93b009a8f 100644 --- a/src/site/twitch.tv/modules/chat-input/ChatInputCarousel.vue +++ b/src/site/twitch.tv/modules/chat-input/ChatInputCarousel.vue @@ -32,10 +32,10 @@ From 3161520c3ba47f8cc10bc1958a980dd6e2354578 Mon Sep 17 00:00:00 2001 From: pimothyxd <29018740+pimothyxd@users.noreply.github.com> Date: Mon, 30 Sep 2024 14:14:09 +0200 Subject: [PATCH 38/66] fix(kick): 7TV badges for chat --- .../kick.com/modules/chat/ChatMessage.vue | 23 +++++++++++++++---- .../kick.com/modules/chat/ChatObserver.vue | 3 ++- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/src/site/kick.com/modules/chat/ChatMessage.vue b/src/site/kick.com/modules/chat/ChatMessage.vue index b86c27204..401b40b6c 100644 --- a/src/site/kick.com/modules/chat/ChatMessage.vue +++ b/src/site/kick.com/modules/chat/ChatMessage.vue @@ -30,10 +30,22 @@ + + + + + + @@ -240,13 +246,65 @@ export const config = [ } .seventv-hide-recommended-channels { - #side-nav > *:nth-child(1) > *:nth-child(1) > *:nth-child(3) { + div[class$="side-nav--collapsed"] + > *:nth-child(1) + > *:nth-child(1) + > *:nth-child(1) + > *:nth-child(3) + > *:nth-child(1) + > *:nth-child(1) + > *:nth-child(2) + > *:nth-child(1) + > *:nth-child(1) + > *:nth-child(1) + > *:nth-child(3) { + display: none !important; + } + + div[class$="side-nav--expanded"] + > *:nth-child(1) + > *:nth-child(1) + > *:nth-child(1) + > *:nth-child(3) + > *:nth-child(1) + > *:nth-child(1) + > *:nth-child(2) + > *:nth-child(1) + > *:nth-child(1) + > *:nth-child(1) + > *:nth-child(4) { display: none !important; } } .seventv-hide-viewers-also-watch { - #side-nav > *:nth-child(1) > *:nth-child(1) > *:nth-child(4) { + div[class$="side-nav--collapsed"] + > *:nth-child(1) + > *:nth-child(1) + > *:nth-child(1) + > *:nth-child(3) + > *:nth-child(1) + > *:nth-child(1) + > *:nth-child(2) + > *:nth-child(1) + > *:nth-child(1) + > *:nth-child(1) + > *:nth-child(4) { + display: none !important; + } + + div[class$="side-nav--expanded"] + > *:nth-child(1) + > *:nth-child(1) + > *:nth-child(1) + > *:nth-child(3) + > *:nth-child(1) + > *:nth-child(1) + > *:nth-child(2) + > *:nth-child(1) + > *:nth-child(1) + > *:nth-child(1) + > *:nth-child(5) { display: none !important; } } @@ -316,5 +374,14 @@ export const config = [ display: none !important; } } + +.seventv-hide-stories { + div[class$="storiesLeftNavSection--csO9S"] { + display: none !important; + } + button:has(div[class$="storiesLeftNavSectionCollapsedButton--txKvw"]) { + display: none !important; + } +} /* stylelint-enable */ diff --git a/src/site/twitch.tv/modules/hidden-elements/hiddenElements.ts b/src/site/twitch.tv/modules/hidden-elements/hiddenElements.ts index a220e40d6..a1a5858bf 100644 --- a/src/site/twitch.tv/modules/hidden-elements/hiddenElements.ts +++ b/src/site/twitch.tv/modules/hidden-elements/hiddenElements.ts @@ -23,6 +23,7 @@ const hidePlayerExtensions = useConfig("player.hide_player_extensions") const hideChannelPointBalanceButton = useConfig("layout.hide_channel_point_balance_button"); const hideOnscreenCelebrations = useConfig("player.hide_onscreen_celebrations"); const hideWhispers = useConfig("layout.hide_whispers"); +const hideStories = useConfig("layout.hide_stories"); export const hiddenElementSettings: Array<{ class: string; isHidden: Ref }> = [ { class: "seventv-hide-leaderboard", isHidden: hideLeaderboard }, { class: "seventv-hide-buttons-below-chatbox", isHidden: hideButtonsBelowChatbox }, @@ -47,4 +48,5 @@ export const hiddenElementSettings: Array<{ class: string; isHidden: Ref hideWhispers.value === 1) }, { class: "seventv-hide-whispers-all", isHidden: computed(() => hideWhispers.value === 2) }, + { class: "seventv-hide-stories", isHidden: hideStories }, ]; From 4ab009c06968dd98e581afdc468bb89d1ce6fbaf Mon Sep 17 00:00:00 2001 From: Excellify Date: Mon, 30 Sep 2024 20:17:24 +0200 Subject: [PATCH 41/66] Kick channel reactivity --- src/common/ReactHooks.ts | 76 +++++++++++++ src/common/Tokenize.ts | 27 ++++- src/common/Transform.ts | 17 +++ src/composable/useLiveQuery.ts | 27 +++-- .../kick.com/modules/chat/ChatController.vue | 15 +-- .../kick.com/modules/chat/ChatMessage.vue | 105 +++--------------- src/site/kick.com/modules/chat/ChatModule.vue | 43 ++++--- .../kick.com/modules/chat/ChatObserver.vue | 76 +++++++------ src/types/kick.module.d.ts | 3 + src/types/react-extended.d.ts | 13 ++- 10 files changed, 230 insertions(+), 172 deletions(-) diff --git a/src/common/ReactHooks.ts b/src/common/ReactHooks.ts index 259926d2f..93c9f2bb4 100644 --- a/src/common/ReactHooks.ts +++ b/src/common/ReactHooks.ts @@ -25,6 +25,15 @@ export function getRootVNode(): ReactExtended.ReactVNode | undefined { return undefined; } +export function getReactProps(element: Element): T | undefined { + for (const k in element) { + if (k.startsWith("__reactProps")) { + const props = Reflect.get(element, k); + return props; + } + } + return undefined; +} /** * Searches the React VDOM tree for a component, starting at the defined node, searching upwards. * @param node React VDOM node to start at @@ -427,6 +436,73 @@ export function useComponentHook( return hook; } +export function getElementFiberStatic

( + elem: Element, + offset: number, +): ReactExtended.ReactFunctionalFiber

| undefined { + const node = getVNodeFromDOM(elem); + if (!node) return; + let current = node; + for (let i = 0; i < offset; i++) { + if (!current.return) return; + current = current.return; + } + if (!current.elementType) return; + + return current as unknown as ReactExtended.ReactFunctionalFiber

; +} + +export async function useStaticRenderFunctionHook

( + selector: string, + offset: number, + func: HookedElementFunction

, +) { + let elem = document.querySelector(selector); + + if (!elem || !getElementFiberStatic(elem, offset)) { + elem = await new Promise((resolve) => { + const observer = new MutationObserver((records) => { + for (const record of records) { + if (!record.addedNodes) continue; + record.addedNodes.forEach((node) => { + if (node instanceof Element && node.querySelector(selector)) { + if (getElementFiberStatic(node, offset)) { + resolve(node); + observer.disconnect(); + return; + } + } + }); + } + }); + observer.observe(document, { childList: true, subtree: true }); + }); + } + + if (!elem) return; + + const fiber = getElementFiberStatic

(elem, offset)!; + + defineFunctionHook(fiber.elementType, "render", func); + func.call(fiber.elementType, null, fiber.pendingProps); + + onUnmounted(() => { + unsetPropertyHook(fiber.elementType, "render"); + }); +} + +type RenderFunction

= (props: P, ref?: React.RefObject) => ReactExtended.ReactRuntimeElement; + +type HookedElementFunction< + P extends object, + E extends ReactExtended.ReactFunctionalFiber

= ReactExtended.ReactFunctionalFiber

, +> = ( + this: E["elementType"], + old: RenderFunction

| null, + props: P, + ref?: React.RefObject, +) => ReactExtended.ReactRuntimeElement | null; + interface ComponentCriteria { parentSelector?: string; childSelector?: string; diff --git a/src/common/Tokenize.ts b/src/common/Tokenize.ts index ee844b6c6..158acd7da 100644 --- a/src/common/Tokenize.ts +++ b/src/common/Tokenize.ts @@ -1,14 +1,30 @@ -import type { AnyToken, ChatUser, EmoteToken, LinkToken, VoidToken } from "@/common/chat/ChatMessage"; +import type { AnyToken, ChatUser, EmoteToken, LinkToken, TextToken, VoidToken } from "@/common/chat/ChatMessage"; +import { convertKickEmote } from "./Transform"; import { parse as tldParse } from "tldts"; const URL_PROTOCOL_REGEXP = /^https?:\/\//i; +const KICK_EMOTE_REGEXP = /^\[emote:(?\d+):(?.+)\]$/; const backwardModifierBlacklist = new Set(["w!", "h!", "v!", "z!"]); +const delimiter = /( )|((?<=\])(?=\[))/; + export function tokenize(opt: TokenizeOptions) { const tokens = [] as AnyToken[]; - const textParts = opt.body.split(" "); + const textParts = opt.body.split(delimiter).filter((part) => !!part); const getEmote = (name: string) => { + if (opt.isKick) { + const match = KICK_EMOTE_REGEXP.exec(name); + if (match && match.groups) { + const { name, id } = match.groups; + return { + name: name, + id: id, + data: convertKickEmote(id, name), + provider: "PLATFORM", + } as SevenTV.ActiveEmote; + } + } if (opt.localEmoteMap?.[name] && Object.hasOwn(opt.localEmoteMap, name)) { return opt.localEmoteMap[name]; } @@ -79,6 +95,12 @@ export function tokenize(opt: TokenizeOptions) { url: parsedUrl.toString(), }, } as LinkToken); + } else { + tokens.push({ + kind: "TEXT", + range: [cursor + 1, next - 1], + content: part, + } as TextToken); } cursor = next; @@ -113,4 +135,5 @@ export interface TokenizeOptions { filteredWords?: string[]; actorUsername?: string; showModifiers?: boolean; + isKick?: boolean; } diff --git a/src/common/Transform.ts b/src/common/Transform.ts index 70bc37067..4e1a5b76b 100644 --- a/src/common/Transform.ts +++ b/src/common/Transform.ts @@ -347,3 +347,20 @@ export function semanticVersionToNumber(ver: string): number { return result; } + +export function convertKickEmote(id: string, name: string): SevenTV.Emote { + return { + id: id, + name: name, + tags: [], + state: [], + lifecycle: 3, + listed: true, + owner: null, + host: { + url: "", + files: [], + srcset: `https://files.kick.com/emotes/${id}/fullsize 1x`, + }, + }; +} diff --git a/src/composable/useLiveQuery.ts b/src/composable/useLiveQuery.ts index 3c287aa16..5cc85949e 100644 --- a/src/composable/useLiveQuery.ts +++ b/src/composable/useLiveQuery.ts @@ -9,11 +9,6 @@ export function useLiveQuery( ) { const value = ref(); - if (opt.reactives) { - opt.reactives.forEach((r) => watch(r, async () => handleResult(await queryFn()), { deep: true })); - watch(queryFn, async () => handleResult(await queryFn())); - } - const handleResult = (result: T | undefined) => { if (!result) return; if (typeof opt.count === "number" && opt.count-- <= 0) { @@ -24,19 +19,31 @@ export function useLiveQuery( onResult?.(result); }; - const observable = liveQuery(queryFn); - const sub = observable.subscribe({ + let observable = liveQuery(queryFn); + let sub = observable.subscribe({ next(x) { handleResult(x); }, }); - tryOnUnmounted(() => sub.unsubscribe()); + const reset = () => { + sub.unsubscribe(); + observable = liveQuery(queryFn); + sub = observable.subscribe({ + next(x) { + handleResult(x); + }, + }); + }; - if (opt.until) { - opt.until.then(() => sub.unsubscribe()); + if (opt.reactives) { + watch(opt.reactives, reset); } + tryOnUnmounted(sub.unsubscribe); + + opt.until?.then(sub.unsubscribe); + return value; } diff --git a/src/site/kick.com/modules/chat/ChatController.vue b/src/site/kick.com/modules/chat/ChatController.vue index 2ddc4900e..618c435b2 100644 --- a/src/site/kick.com/modules/chat/ChatController.vue +++ b/src/site/kick.com/modules/chat/ChatController.vue @@ -10,7 +10,6 @@ diff --git a/src/site/kick.com/modules/chat/ChatObserver.vue b/src/site/kick.com/modules/chat/ChatObserver.vue index e67cd342e..13cda9c8d 100644 --- a/src/site/kick.com/modules/chat/ChatObserver.vue +++ b/src/site/kick.com/modules/chat/ChatObserver.vue @@ -1,3 +1,4 @@ +kj - - - - \ No newline at end of file diff --git a/src/site/kick.com/modules/chat/ChatController.vue b/src/site/kick.com/modules/chat/ChatController.vue index 618c435b2..e21a259b5 100644 --- a/src/site/kick.com/modules/chat/ChatController.vue +++ b/src/site/kick.com/modules/chat/ChatController.vue @@ -1,18 +1,15 @@ diff --git a/src/app/chat/UserCardTabs.vue b/src/app/chat/UserCardTabs.vue index 23b2e4435..01850eb2d 100644 --- a/src/app/chat/UserCardTabs.vue +++ b/src/app/chat/UserCardTabs.vue @@ -15,11 +15,12 @@ diff --git a/src/types/twitch.d.ts b/src/types/twitch.d.ts index 87fff0d77..9f4dbc550 100644 --- a/src/types/twitch.d.ts +++ b/src/types/twitch.d.ts @@ -119,6 +119,11 @@ declare module Twitch { }; }; + export enum WarnUserPopoverPlacement { + CHAT = 0, + VIEWERS_LIST = 1, + } + export type ChatControllerComponent = ReactExtended.WritableComponent<{ authToken: string | undefined; channelDisplayName: string; @@ -161,6 +166,10 @@ declare module Twitch { userDisplayName: string | undefined; userID: string | undefined; userLogin: string | undefined; + warnUserTargetLogin: string | undefined; + warnUserTargetID: string | undefined; + setWarnUserTarget: (target: { targetUserLogin: string; targetUserID: string }) => void; + setWarnUserPopoverPlacement: (placement: WarnUserPopoverPlacement | null) => void; }> & { pushMessage: (msg: Partial) => void; sendMessage: (msg: string, n?: any) => void; From f61fef9ef8f2775592ec8d91e27ae1d62f04947c Mon Sep 17 00:00:00 2001 From: Excellify Date: Tue, 1 Oct 2024 00:20:13 +0200 Subject: [PATCH 53/66] Bump version --- CHANGELOG-nightly.md | 1 + package.json | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG-nightly.md b/CHANGELOG-nightly.md index efddeeb29..a2cf3a6ab 100644 --- a/CHANGELOG-nightly.md +++ b/CHANGELOG-nightly.md @@ -1,6 +1,7 @@ ### 3.1.2.1000 - Implement support for the new Kick site + - Added option to settings to hide Stories from the sidebar - Fixed settings to hide recommended channels and viewers also watch channels - Fixed an issue where history navigation is accidentally triggered during IME composition diff --git a/package.json b/package.json index 4d7ed1c9a..18d0a9ae3 100644 --- a/package.json +++ b/package.json @@ -3,8 +3,8 @@ "displayName": "7TV", "description": "Improve your viewing experience on Twitch & YouTube with new features, emotes, vanity and performance.", "private": true, - "version": "3.1.1", - "dev_version": "4.0", + "version": "3.1.2", + "dev_version": "1.0", "scripts": { "start": "NODE_ENV=dev yarn build:dev && NODE_ENV=dev vite --mode dev", "build:section:app": "vite build --config vite.config.mts", From acf6dff2f0c824e29bf5db00458ae8c0ae96f70e Mon Sep 17 00:00:00 2001 From: Excellify Date: Tue, 1 Oct 2024 18:39:53 +0200 Subject: [PATCH 54/66] Feat/cleanup (#1083) --- CHANGELOG-nightly.md | 9 +- src/app/chat/UserTag.vue | 16 +- src/assets/style/global.scss | 10 ++ src/composable/useCosmetics.ts | 25 +++- src/directive/TextPaintDirective.ts | 25 +++- src/site/global/Changelog.vue | 2 +- src/site/global/Global.vue | 26 ++-- src/site/global/GlobalSettings.ts | 141 ++++++++++++++++++ .../kick.com/modules/chat/ChatMessage.vue | 32 ++-- src/site/kick.com/modules/chat/ChatModule.vue | 46 +----- .../kick.com/modules/chat/ChatObserver.vue | 8 - .../modules/settings/SettingsModule.vue | 3 - src/site/twitch.tv/TwitchSite.vue | 15 +- src/site/twitch.tv/modules/chat/ChatList.vue | 9 +- .../twitch.tv/modules/chat/ChatModule.vue | 73 --------- .../modules/emote-menu/EmoteMenuModule.vue | 40 +---- .../modules/settings/SettingsModule.vue | 19 +-- 17 files changed, 248 insertions(+), 251 deletions(-) create mode 100644 src/site/global/GlobalSettings.ts diff --git a/CHANGELOG-nightly.md b/CHANGELOG-nightly.md index a2cf3a6ab..8ea3c5476 100644 --- a/CHANGELOG-nightly.md +++ b/CHANGELOG-nightly.md @@ -1,7 +1,11 @@ ### 3.1.2.1000 -- Implement support for the new Kick site - +- **Implement support for the new Kick site:** + - Chat emotes + - Emote menu + - Emote auto-completion + - Cosmetics + - 7TV settings - Added option to settings to hide Stories from the sidebar - Fixed settings to hide recommended channels and viewers also watch channels - Fixed an issue where history navigation is accidentally triggered during IME composition @@ -9,6 +13,7 @@ - Added option to highlight messages based on badges - Added button to user card to toggle highlighting for a users messages - Added mod icon for warning users to chat mod icons and user card +- Added an option to limit the drop shadows on paints ### 3.1.1.3000 diff --git a/src/app/chat/UserTag.vue b/src/app/chat/UserTag.vue index 1aafdbbe6..d4c30eaeb 100644 --- a/src/app/chat/UserTag.vue +++ b/src/app/chat/UserTag.vue @@ -22,7 +22,15 @@ type="twitch" @click="handleClick($event)" /> - + @@ -31,7 +39,7 @@ class="seventv-chat-user-username" @click="handleClick($event)" > - + @ {{ user.displayName }} ({{ user.username }}) @@ -146,8 +154,8 @@ const stop = watch( return; } - paint.value = shouldRenderPaint.value && paints && paints.size ? paints.values().next().value : null; - activeBadges.value = shouldRender7tvBadges.value && badges && badges.size ? [...badges.values()] : []; + paint.value = paints && paints.size ? paints.values().next().value : null; + activeBadges.value = badges && badges.size ? [...badges.values()] : []; }, { immediate: true }, ); diff --git a/src/assets/style/global.scss b/src/assets/style/global.scss index 0dd9b6d51..114e3439f 100644 --- a/src/assets/style/global.scss +++ b/src/assets/style/global.scss @@ -89,3 +89,13 @@ body[theme="light"] { -webkit-background-clip: text !important; font-weight: 700; } + +:root.seventv-alternating-chat-lines { + .seventv-chat-observer > div:nth-child(even) { + background-color: var(--seventv-chat-alternate-background-color); + } + + .seventv-chat-list .seventv-message:nth-child(even) { + background-color: var(--seventv-chat-alternate-background-color); + } +} diff --git a/src/composable/useCosmetics.ts b/src/composable/useCosmetics.ts index 6bfc7fa0b..af0d249a4 100644 --- a/src/composable/useCosmetics.ts +++ b/src/composable/useCosmetics.ts @@ -1,4 +1,4 @@ -import { Ref, reactive, ref, toRef } from "vue"; +import { Ref, reactive, ref, toRef, watch } from "vue"; import { until, useTimeout } from "@vueuse/core"; import { DecimalToStringRGBA } from "@/common/Color"; import { log } from "@/common/Logger"; @@ -24,7 +24,13 @@ const data = reactive({ staticallyAssigned: {} as Record | undefined>, }); -const dropShadowRender = useConfig("vanity.paints_drop_shadows"); +const dropShadowRender = useConfig<0 | 1 | 2>("vanity.paints_drop_shadows"); + +watch(dropShadowRender, () => + Object.values(data.cosmetics) + .filter((c) => c.kind === "PAINT") + .forEach((c) => updatePaintStyle(c as SevenTV.Cosmetic<"PAINT">)), +); class CosmeticMap extends Map> { private providers = new Set(); @@ -63,16 +69,18 @@ db.ready().then(async () => { useLiveQuery( () => db.cosmetics.toArray(), (result) => { - data.cosmetics = {}; + const temp = {} as typeof data.cosmetics; for (const cos of result) { - if (data.cosmetics[cos.id]) continue; - data.cosmetics[cos.id] = reactive(cos); + if (temp[cos.id]) continue; + temp[cos.id] = reactive(cos); if (cos.kind === "PAINT") { updatePaintStyle(cos as SevenTV.Cosmetic<"PAINT">); } } + + data.cosmetics = temp; }, ); @@ -386,11 +394,14 @@ export function updatePaintStyle(paint: SevenTV.Cosmetic<"PAINT">, remove = fals const gradients = paint.data.gradients.map((g) => createGradientFromPaint(g)); const filter = (() => { - if (!paint.data.shadows || dropShadowRender.value === false) { + if (!paint.data.shadows || dropShadowRender.value == 0) { return ""; } - return paint.data.shadows.map((v) => createFilterDropshadow(v)).join(" "); + return paint.data.shadows + .slice(0, dropShadowRender.value == 2 ? 1 : undefined) + .map((v) => createFilterDropshadow(v)) + .join(" "); })(); const selector = `.seventv-paint[data-seventv-paint-id="${paint.id}"]`; diff --git a/src/directive/TextPaintDirective.ts b/src/directive/TextPaintDirective.ts index c08d9524f..0c8da8ba3 100644 --- a/src/directive/TextPaintDirective.ts +++ b/src/directive/TextPaintDirective.ts @@ -16,11 +16,26 @@ export const TextPaintDirective = { }, } as Directive; -export function updateElementStyles(el: HTMLElement, paintID: string | null): void { - if (!paintID || (el.hasAttribute(ATTR_SEVENTV_PAINT_ID) && el.getAttribute(ATTR_SEVENTV_PAINT_ID) !== paintID)) { - el.style.backgroundImage = ""; - el.style.filter = ""; - el.style.color = ""; +type PaintedElement = HTMLElement & { + __seventv_backup_style?: { backgroundImage: string; filter: string; color: string }; +}; +export function updateElementStyles(el: PaintedElement, paintID: string | null): void { + const hasPaint = el.hasAttribute(ATTR_SEVENTV_PAINT_ID); + const newPaint = hasPaint && paintID !== el.getAttribute(ATTR_SEVENTV_PAINT_ID); + + if (!hasPaint) { + el.__seventv_backup_style = { + backgroundImage: el.style.backgroundImage, + filter: el.style.filter, + color: el.style.color, + }; + } + + if (hasPaint && newPaint) { + const backup = el.__seventv_backup_style; + el.style.backgroundImage = backup?.backgroundImage ?? ""; + el.style.filter = backup?.filter ?? ""; + el.style.color = backup?.color ?? ""; el.classList.remove("seventv-painted-content"); el.removeAttribute(ATTR_SEVENTV_TEXT); diff --git a/src/site/global/Changelog.vue b/src/site/global/Changelog.vue index e1dd498cf..6b03c9cdb 100644 --- a/src/site/global/Changelog.vue +++ b/src/site/global/Changelog.vue @@ -128,7 +128,7 @@ function updateMediaHref(value: string): string { display: grid; row-gap: 0.5rem; list-style: square; - margin: 0.25rem 0.5rem; + margin: 0.5rem 1rem; } :deep(li) { diff --git a/src/site/global/Global.vue b/src/site/global/Global.vue index 5ab1aea92..d0c2a39d5 100644 --- a/src/site/global/Global.vue +++ b/src/site/global/Global.vue @@ -1,6 +1,9 @@ - - diff --git a/src/site/kick.com/modules/chat/ChatObserver.vue b/src/site/kick.com/modules/chat/ChatObserver.vue index f6ed18997..f0a846863 100644 --- a/src/site/kick.com/modules/chat/ChatObserver.vue +++ b/src/site/kick.com/modules/chat/ChatObserver.vue @@ -267,14 +267,6 @@ onUnmounted(() => { diff --git a/src/site/twitch.tv/modules/chat/ChatModule.vue b/src/site/twitch.tv/modules/chat/ChatModule.vue index d55f606e6..c856416c9 100644 --- a/src/site/twitch.tv/modules/chat/ChatModule.vue +++ b/src/site/twitch.tv/modules/chat/ChatModule.vue @@ -137,43 +137,6 @@ const timestampKeyValues: Record = { const timestampFormatOptions = Object.entries(timestampKeyValues).map(([k, v]) => [v, k]); export const config = [ - declareConfig("general.blur_unlisted_emotes", "TOGGLE", { - path: ["General", ""], - label: "Hide Unlisted Emotes", - hint: "If checked, emotes which have not yet been approved for listing on 7tv.app will be blurred", - defaultValue: false, - }), - declareConfig("chat.emote_margin", "SLIDER", { - path: ["Chat", "Style"], - label: "Emote Spacing", - hint: "Choose the margin around emotes in chat. Negative values lets them overlap and keep the chatlines inline. 0 Makes the emotes not overlap at all", - options: { - min: -1, - max: 1, - step: 0.1, - unit: "rem", - }, - defaultValue: -0.5, - effect(v) { - document.documentElement.style.setProperty("--seventv-emote-margin", `${v}rem`); - }, - }), - declareConfig("chat.emote_scale", "SLIDER", { - path: ["Chat", "Style"], - label: "Emote Scale", - ffz_key: "chat.emotes.2x", - ffz_transform(v: unknown) { - return typeof v === "number" && v > 0 ? 2 : 1; - }, - hint: "Change how large emotes should be in chat, as a multiple of their original size.", - options: { - min: 0.25, - max: 3, - step: 0.25, - unit: "x", - }, - defaultValue: 1, - }), declareConfig("chat.show_emote_modifiers", "TOGGLE", { path: ["Chat", "Style"], label: "Show Emote Modifiers", @@ -263,23 +226,6 @@ export const config = [ hint: "Make the tooltips compact instead of showing the full emote", defaultValue: false, }), - declareConfig("chat.alternating_background", "TOGGLE", { - path: ["Chat", "Style"], - ffz_key: "chat.lines.alternate", - label: "settings.chat_alternating_background.label", - hint: "settings.chat_alternating_background.hint", - defaultValue: false, - }), - declareConfig("chat.alternating_background_color", "COLOR", { - path: ["Chat", "Style"], - label: "Alternating Background Color", - hint: "Configure the color of alternating background (~6% opacity)", - disabledIf: () => !useConfig("chat.alternating_background").value, - effect(v) { - document.body.style.setProperty("--seventv-chat-alternate-background-color", `${v}0f`); - }, - defaultValue: "#808080", - }), declareConfig("chat.padding", "DROPDOWN", { path: ["Chat", "Style"], label: "Padding Style", @@ -494,30 +440,11 @@ export const config = [ }, defaultValue: 10, }), - declareConfig("vanity.nametag_paints", "TOGGLE", { - path: ["Appearance", "Vanity"], - label: "Nametag Paints", - hint: "Whether or not to display nametag paints", - defaultValue: true, - }), - declareConfig("vanity.7tv_Badges", "TOGGLE", { - path: ["Appearance", "Vanity"], - label: "7TV Badges", - hint: "Whether or not to display 7TV Badges", - defaultValue: true, - }), declareConfig("chat.hide_bits_balance", "TOGGLE", { label: "Hide Bits From Community Points Button", hint: "Hide the bits balance from the community points button under the chatbox", path: ["Chat", "Style"], defaultValue: false, }), - declareConfig("vanity.paints_drop_shadows", "TOGGLE", { - path: ["Appearance", "Vanity"], - label: "Drop shadows on paints", - hint: "Wheather or not to display drop shadows on paints (Requires a refresh)", - disabledIf: () => !useConfig("vanity.nametag_paints").value, - defaultValue: true, - }), ]; diff --git a/src/site/twitch.tv/modules/emote-menu/EmoteMenuModule.vue b/src/site/twitch.tv/modules/emote-menu/EmoteMenuModule.vue index 43224484e..5ec504ade 100644 --- a/src/site/twitch.tv/modules/emote-menu/EmoteMenuModule.vue +++ b/src/site/twitch.tv/modules/emote-menu/EmoteMenuModule.vue @@ -71,7 +71,7 @@ const doButtonUpdate = debounceFn((nodes: Element[]) => { watch( placement, - (v) => { + () => { for (const n of Object.values(chatInputController.instances).map((i) => i.domNodes)) { doButtonUpdate(Object.values(n)); } @@ -84,20 +84,6 @@ markAsReady(); diff --git a/src/site/twitch.tv/modules/settings/SettingsModule.vue b/src/site/twitch.tv/modules/settings/SettingsModule.vue index 5278c5ce0..54dd5cbcf 100644 --- a/src/site/twitch.tv/modules/settings/SettingsModule.vue +++ b/src/site/twitch.tv/modules/settings/SettingsModule.vue @@ -1,17 +1,11 @@ - - diff --git a/src/types/twitch.d.ts b/src/types/twitch.d.ts index 9f4dbc550..0a2f4cc35 100644 --- a/src/types/twitch.d.ts +++ b/src/types/twitch.d.ts @@ -720,6 +720,17 @@ declare module Twitch { userLogin: string; }>; + export type SideBarComponent = ReactExtended.WritableComponent<{}> & { + collapseOnBreakpoint: () => void; + }; + + export type SideBarToggleComponent = ReactExtended.WritableComponent<{ + collapsed: boolean; + }> & { + toggle: () => void; + handleToggleVisibility: () => void; + }; + export enum Theme { "Light", "Dark", From b741d906bb644b6c2921dbab93c1bcca7f2d642a Mon Sep 17 00:00:00 2001 From: Jarl Skajaa Gunnarsli Date: Wed, 2 Oct 2024 20:47:37 +0200 Subject: [PATCH 62/66] Bump dev version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 65dd674f6..39734e154 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "Improve your viewing experience on Twitch & YouTube with new features, emotes, vanity and performance.", "private": true, "version": "3.1.2", - "dev_version": "2.0", + "dev_version": "3.0", "scripts": { "start": "NODE_ENV=dev yarn build:dev && NODE_ENV=dev vite --mode dev", "build:section:app": "vite build --config vite.config.mts", From c4972dda479c16d09d39ce8a602a717b2b2d1973 Mon Sep 17 00:00:00 2001 From: Jarl Skajaa Gunnarsli Date: Wed, 2 Oct 2024 20:55:47 +0200 Subject: [PATCH 63/66] Disable css changes to site if option not selected --- .../SidebarPreviewsModule.vue | 31 ++++++++++--------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/src/site/twitch.tv/modules/sidebar-previews/SidebarPreviewsModule.vue b/src/site/twitch.tv/modules/sidebar-previews/SidebarPreviewsModule.vue index 6912ba22d..c4ac63bcb 100644 --- a/src/site/twitch.tv/modules/sidebar-previews/SidebarPreviewsModule.vue +++ b/src/site/twitch.tv/modules/sidebar-previews/SidebarPreviewsModule.vue @@ -53,7 +53,7 @@ function toggle(hovering: boolean) { const sidebarEl = ref(); const isHovering = useElementHover(sidebarEl); -watch(isHovering, toggle); +watch(isHovering, toggle, { immediate: true }); watch( () => sidebar.instances.at(0)?.domNodes["root"], @@ -76,23 +76,26 @@ export const config = [ path: ["Appearance", "Interface"], label: "Sidebar Expand on Hover", hint: "Expand the sidebar when hovering over it.", + effect: (v) => document.body.classList.toggle("seventv-sidebar-hover", v), defaultValue: false, }), ]; From feb0e28ddbdf38783b602438544b9ccce1ceb1e6 Mon Sep 17 00:00:00 2001 From: Excellify Date: Thu, 3 Oct 2024 17:25:29 +0200 Subject: [PATCH 64/66] Update mention color config --- src/app/chat/MessageTokenMention.vue | 13 ++--- src/app/chat/UserTag.vue | 49 +++++++++++++------ src/composable/useSettings.ts | 8 ++- src/composable/useTooltip.ts | 2 +- .../twitch.tv/modules/chat/ChatModule.vue | 16 ++++-- src/types/app.d.ts | 1 + 6 files changed, 59 insertions(+), 30 deletions(-) diff --git a/src/app/chat/MessageTokenMention.vue b/src/app/chat/MessageTokenMention.vue index 6ed7390a3..63ee07343 100644 --- a/src/app/chat/MessageTokenMention.vue +++ b/src/app/chat/MessageTokenMention.vue @@ -9,16 +9,15 @@ color: '', } " - :as-mention="asMention" - :hide-badges="true" - :style="shouldRenderColoredMentions ? { color: token.content.user?.color } : null" + is-mention + :hide-at="!hasAt" + hide-badges /> diff --git a/src/site/twitch.tv/modules/sidebar-previews/SidebarPreviewsModule.vue b/src/site/twitch.tv/modules/sidebar-previews/SidebarPreviewsModule.vue index c4ac63bcb..c5f31336f 100644 --- a/src/site/twitch.tv/modules/sidebar-previews/SidebarPreviewsModule.vue +++ b/src/site/twitch.tv/modules/sidebar-previews/SidebarPreviewsModule.vue @@ -2,6 +2,9 @@ +