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
63 changes: 62 additions & 1 deletion src/components/Hyperchat.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import '../stylesheets/scrollbar.css';
import { onDestroy, afterUpdate, tick } from 'svelte';
import { fade } from 'svelte/transition';
import { get } from 'svelte/store';
import dark from 'smelte/src/dark';
import WelcomeMessage from './WelcomeMessage.svelte';
import Message from './Message.svelte';
Expand All @@ -24,6 +25,7 @@
ChatUserActions
} from '../ts/chat-constants';
import { isAllEmoji, isChatMessage, isPrivileged, responseIsAction } from '../ts/chat-utils';
import { handleReplyThreadResponse } from '../ts/chat-actions';
import Button from 'smelte/src/components/Button';
import {
theme,
Expand All @@ -41,6 +43,9 @@
selfChannel,
alertDialog,
stickySuperchats,
activeReplyThreadId,
liveReplyBuffer,
liveLikeCounts,
currentProgress,
enableStickySuperchatBar,
lastOpenedVersion,
Expand Down Expand Up @@ -141,6 +146,31 @@
piledMessages = [];
}

const stickyLikeKeys = (sticky: Ytc.ParsedTicker[]): Set<string> => {
const keys = new Set<string>();
sticky.forEach(sc => {
if (sc.likeCountEntityKey) keys.add(sc.likeCountEntityKey);
});
return keys;
};

const pruneLikeCounts = (sticky: Ytc.ParsedTicker[]) => {
const current = get(liveLikeCounts);
if (current.size === 0) return;
const keep = stickyLikeKeys(sticky);
let mutated = false;
const next = new Map(current);
next.forEach((_, k) => {
if (!keep.has(k)) {
next.delete(k);
mutated = true;
}
});
if (mutated) liveLikeCounts.set(next);
};

$: pruneLikeCounts($stickySuperchats);


const onBonk = (bonk: Ytc.ParsedBonk) => {
messageActions.forEach((action) => {
Expand All @@ -151,22 +181,37 @@
});
};

const LIVE_REPLY_BUFFER_LIMIT = 200;

const filterTickers = (items: Chat.MessageAction[]): Chat.MessageAction[] => {
const keep: Chat.MessageAction[] = [];
const discard: Ytc.ParsedTicker[] = [];
const newLiveReplies: Ytc.ParsedMessage[] = [];
const trackedThreadId = $activeReplyThreadId;
items.forEach(item => {
if ('tickerDuration' in item.message) {
if (!$stickySuperchats.some(sc => sc.messageId === item.message.messageId)) {
discard.push(item.message);
}
} else keep.push(item);
} else {
keep.push(item);
if (trackedThreadId && item.message.replyToSuperchat?.threadId === trackedThreadId) {
newLiveReplies.push(item.message);
}
}
});
if ($enableStickySuperchatBar && discard.length) {
$stickySuperchats = [
...discard,
...$stickySuperchats
];
}
if (newLiveReplies.length > 0) {
const combined = [...$liveReplyBuffer, ...newLiveReplies];
$liveReplyBuffer = combined.length > LIVE_REPLY_BUFFER_LIMIT
? combined.slice(combined.length - LIVE_REPLY_BUFFER_LIMIT)
: combined;
}
return keep;
};

Expand Down Expand Up @@ -232,6 +277,19 @@
messageActions = [...messageActions, welcome];
}
break;
case 'likeCounts': {
const knownKeys = stickyLikeKeys($stickySuperchats);
if (knownKeys.size === 0) break;
let next: Map<string, number> | null = null;
for (const [key, count] of Object.entries(action.counts)) {
if (knownKeys.has(key) && $liveLikeCounts.get(key) !== count) {
if (!next) next = new Map($liveLikeCounts);
next.set(key, count);
}
}
if (next) $liveLikeCounts = next;
break;
}
}
};

Expand Down Expand Up @@ -290,6 +348,9 @@
break;
case 'registerClientResponse':
break;
case 'replyThreadResponse':
handleReplyThreadResponse(response);
break;
default:
console.error('Unknown payload type', { port, response });
break;
Expand Down
37 changes: 19 additions & 18 deletions src/components/MembershipItem.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@
import Message from './Message.svelte';
import MessageRun from './MessageRuns.svelte';
import { formatAuthorName } from '../ts/component-utils';
import { showProfileIcons } from '../ts/storage';
import { showProfileIcons, showTimestamps } from '../ts/storage';
import { membershipBackground, milestoneChatBackground } from '../ts/chat-constants';

export let message: Ytc.ParsedMessage;

const classes = 'inline-flex flex-col rounded break-words overflow-hidden w-full text-white';
const classes = 'relative inline-flex flex-col rounded break-words overflow-hidden w-full text-white';

$: membership = message.membership;
$: membershipGift = message.membershipGiftPurchase;
Expand All @@ -22,8 +22,16 @@

{#if membership || membershipGift}
<div class={classes} style="background-color: #{membershipBackground};">
{#if membershipGift}
<img
class="absolute inset-y-0 right-0 h-full w-auto pointer-events-none select-none z-0"
style="opacity: 0.4;"
src={membershipGift.image.src}
alt=""
aria-hidden="true" />
{/if}
<div
class="p-2"
class="p-2 relative z-10"
style="{isMilestoneChat ? `background-color: #${milestoneChatBackground};` : ''}"
>
{#if $showProfileIcons}
Expand All @@ -33,28 +41,21 @@
alt={message.author.profileIcon.alt}
/>
{/if}
<span class="font-bold tracking-wide align-middle mr-3">
{#if $showTimestamps}
<span class="mr-1 text-xs opacity-75 align-middle">{message.timestamp}</span>
{/if}
<span class="font-bold tracking-wide align-middle">
{displayAuthorName}
</span>
{#if primaryText && primaryText.length > 0}
<MessageRun
class="font-medium mr-3"
runs={primaryText}
/>
{/if}
{#if membership}
<MessageRun runs={membership.headerSubtext} />
<MessageRun class="float-right align-middle ml-2" runs={membership.headerSubtext} />
{/if}
{#if membershipGift}
<img
class="h-10 w-10 float-right"
src={membershipGift.image.src}
alt={membershipGift.image.alt}
title={membershipGift.image.alt} />
{#if primaryText && primaryText.length > 0}
<MessageRun class="font-medium block clear-both" runs={primaryText} />
{/if}
</div>
{#if isMilestoneChat}
<div class="p-2">
<div class="p-2 relative z-10">
<Message message={message} hideName />
</div>
{/if}
Expand Down
39 changes: 37 additions & 2 deletions src/components/Message.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,22 @@
showUserBadges,
hoveredItem,
port,
selfChannelId
selfChannelId,
showSuperchatReplyIndicators,
stickySuperchats,
focusedSuperchat
} from '../ts/storage';
import { chatUserActionsItems, ChatUserActions, Theme } from '../ts/chat-constants';
import { useBanHammer } from '../ts/chat-actions';
import { formatAuthorName } from '../ts/component-utils';
import { mdiGift } from '@mdi/js';
import { mdiGift, mdiReply } from '@mdi/js';

export let message: Ytc.ParsedMessage;
export let deleted: Chat.MessageDeletedObj | null = null;
export let forceDark = false;
export let hideName = false;
export let hideDropdown = false;
export let hideReplyIndicator = false;

const nameClass = 'font-bold tracking-wide align-middle';
const generateNameColorClass = (member: boolean, moderator: boolean, owner: boolean, forceDark: boolean) => {
Expand Down Expand Up @@ -78,6 +82,13 @@
value: d.value.toString(),
onClick: () => useBanHammer(message, d.value, $port)
}));

const openReplyTargetSuperchat = () => {
const threadId = message.replyToSuperchat?.threadId;
const match = threadId ? $stickySuperchats.find((s) => s.threadId === threadId) : undefined;
if (!threadId || !match) return;
$focusedSuperchat = match;
};
</script>

<!-- svelte-ignore a11y-mouse-events-have-key-events -->
Expand Down Expand Up @@ -141,6 +152,30 @@
</span>
<span class="mr-1.5" class:hidden={!showUserMargin} />
{/if}
{#if message.replyToSuperchat && $showSuperchatReplyIndicators && !hideReplyIndicator}
<!-- svelte-ignore a11y-click-events-have-key-events -->
<span
class="inline-flex items-center justify-center align-middle cursor-pointer rounded"
style={
`width: 1.6em; height: 1.6em;` +
(message.replyToSuperchat.bgColor ? ` background-color: #${message.replyToSuperchat.bgColor};` : '') +
(message.replyToSuperchat.fgColor ? ` color: #${message.replyToSuperchat.fgColor};` : ' color: inherit;')
}
role="button"
tabindex="0"
aria-label={`Open Super Chat by ${message.replyToSuperchat.authorName}`}
title={`Open Super Chat by ${message.replyToSuperchat.authorName}`}
on:click|stopPropagation={openReplyTargetSuperchat}
>
<svg
height="1.2em"
width="1.2em"
viewBox="0 0 24 24"
>
<path d={mdiReply} fill="currentColor"/>
</svg>
</span>
{/if}
<MessageRun
runs={message.message}
{forceDark}
Expand Down
24 changes: 15 additions & 9 deletions src/components/PaidMessage.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import isDarkColor from 'is-dark-color';
import { Theme } from '../ts/chat-constants';
import { formatAuthorName } from '../ts/component-utils';
import { showProfileIcons } from '../ts/storage';
import { showProfileIcons, showTimestamps } from '../ts/storage';

export let message: Ytc.ParsedMessage;

Expand All @@ -22,7 +22,8 @@
headerStyle = '';
}

const classes = 'inline-flex flex-col rounded break-words overflow-hidden w-full';
const classes = 'inline-flex flex-col rounded break-words w-full';
$: hasBody = message.message.length > 0 || !!message.superSticker;
$: displayAuthorName = formatAuthorName(message.author.name);

$: if (!paid) {
Expand All @@ -36,26 +37,31 @@

{#if paid}
<div class={classes} style={backgroundColor + textColor}>
<div class="p-2" style={headerStyle}>
<div class="p-2 {hasBody ? 'rounded-t' : 'rounded'}" style={headerStyle}>
{#if $showProfileIcons}
<img
class="h-5 w-5 inline align-middle rounded-full flex-none mr-1"
src={message.author.profileIcon.src}
alt={message.author.profileIcon.alt}
/>
{/if}
<span class="mr-1 underline font-bold">{amount}</span>
<span class="font-bold tracking-wide" style={nameColor}>
{#if $showTimestamps}
<span class="mr-1 text-xs opacity-75 align-middle">{message.timestamp}</span>
{/if}
<span class="font-bold tracking-wide align-middle" style={nameColor}>
{displayAuthorName}
</span>
{#if message.superSticker}
<span class="float-right underline font-bold align-middle ml-2">{amount}</span>
</div>
{#if message.superSticker}
<div class="p-2">
<img
class="h-10 w-10 float-right"
class="h-full w-auto max-h-20"
src={message.superSticker.src}
alt={message.superSticker.alt}
title={message.superSticker.alt} />
{/if}
</div>
</div>
{/if}
{#if message.message.length > 0}
<div class="p-2">
<Message message={message} hideName forceTLColor={darkEval()} />
Expand Down
Loading
Loading