A follow button in a Kick channel's header

Beside what the stream is: a heart that fills when this account follows,
and does nothing but say so when it already does. Absent for an account
that cannot answer the question rather than offering an action that would
fail.
This commit is contained in:
2026-09-03 21:25:41 -04:00
parent 82291f3ff5
commit 5f817d3511
4 changed files with 46 additions and 1 deletions
+1 -1
Submodule nobilis updated: 39f8ae3912...df660ea040
+15
View File
@@ -4665,3 +4665,18 @@ video.lightbox-media {
.stream-chip .ellipsis {
min-width: 2em;
}
/* Following a Kick channel. Quiet when already following - it is a state
being shown rather than an action being offered - and only ever coloured
by the heart, so the header does not gain a second bright thing. */
.follow-button {
display: inline-flex;
align-items: center;
gap: var(--spacing-xxs);
padding: 1px var(--spacing-xs);
font-size: var(--font-size-small);
}
.follow-button.following {
color: var(--error);
}
@@ -210,6 +210,23 @@ export function ConversationTools({ buffer }: { buffer: BufferEntry }): JSX.Elem
game and the number of people watching are the context the chat is
missing - and "offline" is worth saying too, since a quiet channel
and an off-air one look identical otherwise. */}
{/* Following is the one thing about a Kick channel somebody changes from
here - subscribing is money and raiding is the broadcaster's own
gesture, and neither belongs behind a button in a chat client.
Absent for an account that cannot say, rather than offering to do
something that would fail. */}
{stream && stream.following !== null && stream.following !== undefined && (
<button
type="button"
className={classes('button', 'subtle', 'follow-button', stream.following && 'following')}
title={stream.following ? 'Stop following this channel' : 'Follow this channel'}
onClick={() => store.setFollowing(buffer.id, !stream.following)}
>
<Icon name={stream.following ? 'favorite' : 'favorite_border'} size={15} />
{stream.following ? 'Following' : 'Follow'}
</button>
)}
{stream && (
<span
className={classes('stream-chip', 'small', stream.live && 'live')}
+13
View File
@@ -116,6 +116,12 @@ export interface KickStream {
/** Unix seconds the stream started, for "live for 2h 14m". */
startedTs?: number | null
followers?: number | null
/**
* Whether this account follows the channel, or absent when it cannot say -
* a Kick account that is not signed in reads chat and knows nothing about
* follows, and a button offering to follow there would fail when pressed.
*/
following?: boolean | null
}
export type ActivePanel = '' | 'accounts' | 'settings' | 'join' | 'downloads'
@@ -1256,6 +1262,13 @@ export class ChatStore {
.catch((e: Error) => this.toast('error', e.message))
}
/** Follows a Kick channel, or stops. The header reads the answer back. */
setFollowing(bufferId: string, follow: boolean): void {
void window.moho
.rpc('setKickFollowing', { bufferId, follow })
.catch((e: Error) => this.toast('error', e.message))
}
private handleEvent(frame: NobilisEvent): void {
const { event, data } = frame
switch (event) {