Skip to content

Commit

Permalink
Reactive paints and badges
Browse files Browse the repository at this point in the history
  • Loading branch information
Excellify committed Oct 1, 2024
1 parent cf0d961 commit 1cb5042
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 21 deletions.
16 changes: 12 additions & 4 deletions src/composable/useCosmetics.ts
Original file line number Diff line number Diff line change
@@ -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";
Expand Down Expand Up @@ -26,6 +26,12 @@ const data = reactive({
});
const dropShadowRender = useConfig("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<T extends SevenTV.CosmeticKind> extends Map<string, SevenTV.Cosmetic<T>> {
private providers = new Set<SevenTV.Provider>();

Expand Down Expand Up @@ -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;
},
);

Expand Down
25 changes: 20 additions & 5 deletions src/directive/TextPaintDirective.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,26 @@ export const TextPaintDirective = {
},
} as Directive<HTMLElement, string | null>;

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 && (!paintID || el.getAttribute(ATTR_SEVENTV_PAINT_ID) !== paintID)) {
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);
Expand Down
32 changes: 20 additions & 12 deletions src/site/kick.com/modules/chat/ChatMessage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
</Teleport>
</template>

<Teleport :to="badgeContainer">
<Teleport v-if="shouldRenderBadges" :to="badgeContainer">
<span v-if="cosmetics.badges.size" class="seventv-badge-list">
<Badge
v-for="[id, badge] of cosmetics.badges"
Expand All @@ -36,7 +36,7 @@
</template>

<script setup lang="ts">
import { nextTick, onMounted, reactive, watch, watchEffect } from "vue";
import { nextTick, onMounted, reactive, watch } from "vue";
import { ref } from "vue";
import { onUnmounted } from "vue";
import { useEventListener } from "@vueuse/core";
Expand All @@ -47,6 +47,7 @@ import { IsEmoteToken, IsLinkToken, IsTextToken } from "@/common/type-predicates
import { useChannelContext } from "@/composable/channel/useChannelContext";
import { useChatEmotes } from "@/composable/chat/useChatEmotes";
import { useCosmetics } from "@/composable/useCosmetics";
import { useConfig } from "@/composable/useSettings";
import Badge from "@/app/chat/Badge.vue";
import Emote from "@/app/chat/Emote.vue";
import { updateElementStyles } from "@/directive/TextPaintDirective";
Expand Down Expand Up @@ -77,6 +78,9 @@ const badgeContainer = document.createElement("seventv-container");
const containers = ref<HTMLElement[]>([]);
const tokens = reactive<WeakMap<HTMLElement, AnyToken[]>>(new WeakMap());
const shouldRenderPaints = useConfig<boolean>("vanity.nametag_paints");
const shouldRenderBadges = useConfig<boolean>("vanity.7tv_Badges");
// Listen for click events
useEventListener(props.bind.usernameEl.parentElement, "click", () => {
emit("open-card", props.bind);
Expand All @@ -89,12 +93,6 @@ function textElToMessage(el: HTMLElement) {
// Process kick's text entries into a containerized token
function process(): void {
props.bind.usernameEl.insertAdjacentElement("beforebegin", badgeContainer);
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 = textElToMessage(el);
Expand All @@ -120,10 +118,20 @@ function process(): void {
nextTick(() => emit("render"));
}
watch(cosmetics, process);
watchEffect(process);
onMounted(process);
watch(() => props.bind.texts, process, { immediate: true });
watch(
() => props.bind.usernameEl,
(el) => {
if (!el.contains(badgeContainer)) el.insertAdjacentElement("beforebegin", badgeContainer);
},
{ immediate: true },
);
watch(
[() => props.bind.usernameEl, cosmetics, shouldRenderPaints],
([el, c, s]) => updateElementStyles(el, s && c.paints.size ? Array.from(c.paints.values())[0].id : null),
{ immediate: true },
);
onUnmounted(() => {
for (const el of containers.value) {
Expand Down

0 comments on commit 1cb5042

Please sign in to comment.