diff --git a/nobilis b/nobilis index 1bd675c..20d087d 160000 --- a/nobilis +++ b/nobilis @@ -1 +1 @@ -Subproject commit 1bd675c32c457684c42061529836d59d37ca18f3 +Subproject commit 20d087d0776b977d02961076db04ac6e22a3c9e2 diff --git a/src/renderer/src/components/BufferList.tsx b/src/renderer/src/components/BufferList.tsx index dea1e67..4b1723d 100644 --- a/src/renderer/src/components/BufferList.tsx +++ b/src/renderer/src/components/BufferList.tsx @@ -277,6 +277,11 @@ export function BufferList(): JSX.Element { : undefined } autojoins={store.autojoins(b.accountId, bufferDisplayName(b.name))} + onOpenInBrowser={ + b.accountId.startsWith('kick:') + ? () => void window.moho.openExternal(`https://kick.com/${bufferDisplayName(b.name)}`) + : undefined + } onHide={() => hideBuffer(b.id)} onClose={() => void store.closeBuffer(b.id)} inCall={voiceSessions.some((s) => s.bufferId === b.id)} @@ -678,6 +683,8 @@ interface BufferRowProps { onToggleMute: () => void /** IRC channels only: rejoin this on every connect, or stop doing so. */ onToggleAutojoin?: () => void + /** Kick channels only: watch the stream where streams are watched. */ + onOpenInBrowser?: () => void /** Whether it is in that list now. */ autojoins?: boolean onHide: () => void @@ -715,6 +722,7 @@ function BufferRow({ onToggleMute, onToggleAutojoin, autojoins, + onOpenInBrowser, onHide, onClose, onCall, @@ -776,6 +784,12 @@ function BufferRow({ poppedOut ? { label: 'Close its window', icon: 'close_fullscreen', onClick: onDock } : { label: 'Open in a new window', icon: 'open_in_new', onClick: onPopOut }, + // A Kick channel is a stream as well as a chat, and the stream is not + // something this client shows - so the way to watch it belongs on the + // row, next to the way to open its chat in a window. + ...(onOpenInBrowser + ? ([{ label: 'Open in browser', icon: 'public', onClick: onOpenInBrowser }] as MenuEntry[]) + : []), { separator: true }, // Which channels an account rejoins on connect is a property of the // channels, so it is set on one - it used to be a comma-separated field diff --git a/src/renderer/src/lib/richtext.tsx b/src/renderer/src/lib/richtext.tsx index 90fada2..b32d607 100644 --- a/src/renderer/src/lib/richtext.tsx +++ b/src/renderer/src/lib/richtext.tsx @@ -216,8 +216,11 @@ function walk(node: Node, handlers: Handlers, depth: number): ReactNode[] { break } - // A link to a channel is handled here rather than handed to the OS, - // which would bounce it straight back to this window. + // A link to a channel is acted on here rather than handed straight to + // the OS. What that means differs by service: an irc: link belongs to + // this client alone, while a kick.com link is a web page as well as a + // channel, so following it opens the stream in the browser *and* the + // chat here - see the store's followDeepLink. if (isDeepLink(href)) { out.push( a.service === 'kick' && a.hasPassword) ?? this.state.accounts.find((a) => a.service === 'kick') if (!account) { - this.toast('error', 'Add a Kick account to open that link here') - void window.moho.openExternal(url) + // The stream is already opening; only the chat is missing, and + // saying so is better than a silence that looks like half of it + // failed. + this.toast('info', 'Add a Kick account to open that chat here too') return } this.set({ activePanel: '' })