Badges belong to the byline, not to the message
They were siblings of the name, and the name is a block in the comfy and bubble layouts - so the badges dropped to the next line and sat in with the message text, reading as part of what was said. Name and badges are one element now, so a layout that gives the name its own line takes the badges with it. The compact log keeps the byline inline, where the name has always run into the text. The member panel is also named for what it actually is on Kick: there is no viewer list to ask for, so it lists who has spoken recently and says so. Kick's badges are words rather than sigils, mapped onto the same ranks the list already groups by - one grouping for every protocol rather than each growing its own. Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
+1
-1
Submodule nobilis updated: 0cd38dfabd...f78d18c85c
@@ -470,9 +470,12 @@
|
||||
|
||||
/* A name above the bubble, not inside it: it labels who is speaking, and
|
||||
inside it would read as part of what they said. */
|
||||
.message-row.bubbles .message-from {
|
||||
.message-row.bubbles .message-byline {
|
||||
display: block;
|
||||
margin: 0 0 2px;
|
||||
}
|
||||
|
||||
.message-row.bubbles .message-from {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
@@ -568,12 +571,20 @@
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.message-from {
|
||||
font-weight: 600;
|
||||
/* The name and whatever sits with it. One element so a layout that puts the
|
||||
name on its own line takes the badges with it - as siblings they dropped to
|
||||
the next line and read as part of the message. */
|
||||
.message-byline {
|
||||
margin-right: var(--spacing-s);
|
||||
}
|
||||
|
||||
.message-row.comfy .message-from {
|
||||
.message-from {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* Comfy and bubble layouts give the byline its own line; the compact log
|
||||
keeps it inline, where the name has always run into the text. */
|
||||
.message-row.comfy .message-byline {
|
||||
display: block;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
@@ -367,8 +367,12 @@ export function MessageRow({
|
||||
Badges beside it, because on Kick who is talking is half the
|
||||
message: a moderator, a two-year subscriber and a stranger read
|
||||
as three different things. */}
|
||||
{/* Name and badges in one element, because the name is a block in
|
||||
comfy and bubble layouts - badges as siblings of it fell to the
|
||||
next line and sat in with the message text. They belong to the
|
||||
byline, so they live in it. */}
|
||||
{!grouped && !isSystem && !own && (
|
||||
<>
|
||||
<span className="message-byline">
|
||||
<span className="message-from" style={{ color: message.senderColor || nickColor(message.from) }}>
|
||||
{message.isAction ? `* ${message.from}` : message.from}
|
||||
</span>
|
||||
@@ -381,7 +385,7 @@ export function MessageRow({
|
||||
{badgeLabel(badge)}
|
||||
</span>
|
||||
))}
|
||||
</>
|
||||
</span>
|
||||
)}
|
||||
|
||||
{/* Said in words as well as in styling. Italics and a tinted edge
|
||||
|
||||
@@ -28,6 +28,15 @@ import type { Member } from '../../../shared/wire'
|
||||
*/
|
||||
export type ModerationAction = 'kick' | 'ban' | 'mute' | 'op' | 'deop' | 'voice'
|
||||
|
||||
/**
|
||||
* Why the list says what it says, for the one service where it needs saying.
|
||||
*/
|
||||
function rosterNote(service: string | undefined): string | undefined {
|
||||
return service === 'kick'
|
||||
? 'Kick has no viewer list — this is everybody who has spoken here recently'
|
||||
: undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* What the local user may do in an IRC channel, read off their own prefix.
|
||||
*
|
||||
@@ -95,7 +104,15 @@ export function NickList(): JSX.Element {
|
||||
// Guard the empty prefix explicitly: indexOf('') is 0, which is the
|
||||
// owner slot, so every unranked member - all of Sneedchat, and any
|
||||
// ordinary IRC user - was being filed under Owners.
|
||||
const rank = (m: Member): number => (m.prefix ? '~&@%+'.indexOf(m.prefix) : -1)
|
||||
//
|
||||
// Kick says the same thing in words rather than symbols, since its badges
|
||||
// have names and no agreed sigils. Mapped onto the same ranks so one
|
||||
// grouping serves both rather than each protocol growing its own.
|
||||
const rank = (m: Member): number => {
|
||||
if (!m.prefix) return -1
|
||||
const named: Record<string, number> = { broadcaster: 0, moderator: 2, vip: 3, og: 4, founder: 4, subscriber: 4 }
|
||||
return m.prefix in named ? named[m.prefix] : '~&@%+'.indexOf(m.prefix)
|
||||
}
|
||||
return [
|
||||
{ label: 'Owners', members: filtered.filter((m) => rank(m) === 0).sort(byName) },
|
||||
{ label: 'Moderators', members: filtered.filter((m) => rank(m) > 0 && rank(m) < 4).sort(byName) },
|
||||
@@ -121,7 +138,12 @@ export function NickList(): JSX.Element {
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<span className="small muted ellipsis">{members.length} members</span>
|
||||
// Named for what the list actually is. Kick has no viewer list to
|
||||
// ask for - it is built from who has spoken - and calling that
|
||||
// "members" would claim to know something nobody here can know.
|
||||
<span className="small muted ellipsis" title={rosterNote(account?.service)}>
|
||||
{members.length} {account?.service === 'kick' ? 'recently talking' : 'members'}
|
||||
</span>
|
||||
)}
|
||||
<IconButton
|
||||
name={searchActive ? 'close' : 'search'}
|
||||
|
||||
Reference in New Issue
Block a user