Offer the IRC networks people actually join
Issues #12 and #13. Typing a hostname and port correctly is the one step of adding an IRC account where a small mistake looks like the server being down, so the common networks are offered ready-made and fill the fields - which stay editable, and a network not on the list is typed in exactly as before. The same table gives each of those networks its own colour and letters in the rail, instead of the identical "#" every IRC account shared - telling one entry from another being the thing that column exists for. Initials rather than logos, deliberately: those belong to the networks that own them, and a wrong-looking copy of somebody's logo is worse than two clean letters. Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
@@ -3,6 +3,7 @@ import { Icon, IconButton, MaskIcon } from './Icon'
|
||||
import { MatrixAccountTools } from './MatrixAccountTools'
|
||||
import { useChat, usePref, useStore } from '../state/hooks'
|
||||
import { bufferDisplayName, resolveMediaUrl, serviceIcon, serviceLabel } from '../lib/util'
|
||||
import { IRC_NETWORKS, ircNetworkFor } from '../lib/networks'
|
||||
import type { Account } from '../../../shared/wire'
|
||||
|
||||
const ADDABLE = ['irc', 'discord', 'sockchat', 'matrix'] as const
|
||||
@@ -380,6 +381,32 @@ function IrcForm({ onDone }: { onDone: () => void }): JSX.Element {
|
||||
<span className="small muted">Nickname</span>
|
||||
<input className="text-field" value={nick} onChange={(e) => setNick(e.target.value)} />
|
||||
</label>
|
||||
{/* Typing a hostname and port correctly is the one step here where a
|
||||
small mistake looks like the server being down, so the networks
|
||||
people actually join are offered ready-made. Choosing one fills
|
||||
the fields rather than hiding them: they stay editable, and a
|
||||
network not on the list is typed in exactly as before. */}
|
||||
<label className="field">
|
||||
<span className="small muted">Network</span>
|
||||
<select
|
||||
className="text-field"
|
||||
value={ircNetworkFor(host)?.id ?? ''}
|
||||
onChange={(e) => {
|
||||
const n = IRC_NETWORKS.find((x) => x.id === e.target.value)
|
||||
if (!n) return
|
||||
setHost(n.host)
|
||||
setPort(String(n.port))
|
||||
setSsl(n.tls)
|
||||
}}
|
||||
>
|
||||
<option value="">Other…</option>
|
||||
{IRC_NETWORKS.map((n) => (
|
||||
<option key={n.id} value={n.id}>
|
||||
{n.name}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
<label className="field">
|
||||
<span className="small muted">Server</span>
|
||||
<input
|
||||
|
||||
@@ -3,6 +3,7 @@ import { ContextMenu, useContextMenu } from './ContextMenu'
|
||||
import { Icon, IconButton, MaskIcon } from './Icon'
|
||||
import { Avatar } from './Avatar'
|
||||
import { LeaveConfirm } from './LeaveConfirm'
|
||||
import { ircNetworkFor } from '../lib/networks'
|
||||
import { useChat, usePref, useStore } from '../state/hooks'
|
||||
import { bufferDisplayName, classes, nickColor, resolveMediaUrl, serviceIcon } from '../lib/util'
|
||||
import {
|
||||
@@ -92,6 +93,19 @@ function GroupFace({ group, customIcon }: { group: RailGroup; customIcon?: strin
|
||||
return <img className="rail-mark" src={service.mark} alt="" />
|
||||
}
|
||||
if (group.kind === 'account') {
|
||||
// A known IRC network gets its own colour and letters rather than the
|
||||
// same generic "#" every network would otherwise share - which is the
|
||||
// one thing the rail exists to avoid. Initials rather than logos: those
|
||||
// belong to the networks that own them, and a wrong-looking copy of
|
||||
// somebody's logo is worse than two clean letters.
|
||||
const network = group.service === 'irc' ? ircNetworkFor(group.name) : null
|
||||
if (network) {
|
||||
return (
|
||||
<span className="rail-initials" style={{ color: network.colour }}>
|
||||
{network.mark}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
return service.mark ? <MaskIcon src={service.mark} size={22} /> : <Icon name={service.glyph!} size={22} />
|
||||
}
|
||||
return (
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* The IRC networks somebody is actually likely to be joining.
|
||||
*
|
||||
* Typing a hostname and a port correctly is the one step of adding an IRC
|
||||
* account where a small mistake looks like the server being down, so the
|
||||
* common ones are offered ready-made. Deliberately short: this is a shortcut
|
||||
* for the networks people use, not a directory - "Other" is always there and
|
||||
* is what every network not on the list uses.
|
||||
*
|
||||
* Colours are the network's own where it has a recognisable one, and are used
|
||||
* to tell one server tile from another in the rail. Marks are initials rather
|
||||
* than logos: those belong to the networks that own them, and a wrong-looking
|
||||
* copy of somebody's logo is worse than two clean letters.
|
||||
*/
|
||||
export interface IrcNetwork {
|
||||
id: string
|
||||
name: string
|
||||
host: string
|
||||
port: number
|
||||
tls: boolean
|
||||
/** Two characters at most, for the rail tile. */
|
||||
mark: string
|
||||
colour: string
|
||||
}
|
||||
|
||||
export const IRC_NETWORKS: IrcNetwork[] = [
|
||||
{ id: 'libera', name: 'Libera.Chat', host: 'irc.libera.chat', port: 6697, tls: true, mark: 'Li', colour: '#7f4bd8' },
|
||||
{ id: 'oftc', name: 'OFTC', host: 'irc.oftc.net', port: 6697, tls: true, mark: 'OF', colour: '#2f7fbf' },
|
||||
{ id: 'rizon', name: 'Rizon', host: 'irc.rizon.net', port: 6697, tls: true, mark: 'Ri', colour: '#c94f4f' },
|
||||
{ id: 'quakenet', name: 'QuakeNet', host: 'irc.quakenet.org', port: 6667, tls: false, mark: 'QN', colour: '#d08a2a' },
|
||||
{ id: 'efnet', name: 'EFnet', host: 'irc.efnet.org', port: 6697, tls: true, mark: 'EF', colour: '#4c8f6b' },
|
||||
{ id: 'undernet', name: 'Undernet', host: 'irc.undernet.org', port: 6697, tls: true, mark: 'UN', colour: '#3f6fa8' },
|
||||
{ id: 'snoonet', name: 'Snoonet', host: 'irc.snoonet.org', port: 6697, tls: true, mark: 'Sn', colour: '#e06a3a' },
|
||||
{ id: 'hackint', name: 'hackint', host: 'irc.hackint.org', port: 6697, tls: true, mark: 'hi', colour: '#5f9ea0' }
|
||||
]
|
||||
|
||||
/**
|
||||
* The network a hostname belongs to, or null.
|
||||
*
|
||||
* Matched on the registrable part of the host rather than the whole thing,
|
||||
* since a network answers on many names - irc.libera.chat, but also
|
||||
* ipv6.libera.chat and any number of round-robin aliases.
|
||||
*/
|
||||
export function ircNetworkFor(host: string): IrcNetwork | null {
|
||||
const h = host.toLowerCase().replace(/:\d+$/, '')
|
||||
return (
|
||||
IRC_NETWORKS.find((n) => {
|
||||
const domain = n.host.split('.').slice(-2).join('.')
|
||||
return h === n.host || h.endsWith(`.${domain}`) || h === domain
|
||||
}) ?? null
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user