diff --git a/nobilis b/nobilis index 5ddd786..c04d211 160000 --- a/nobilis +++ b/nobilis @@ -1 +1 @@ -Subproject commit 5ddd7860b27ad0381cfa6b604afba4192604ef11 +Subproject commit c04d211b4439fd9d8d7b5bdf2c26368233d363ea diff --git a/src/renderer/src/components/MessageRow.tsx b/src/renderer/src/components/MessageRow.tsx index b8da5e0..bc5ee00 100644 --- a/src/renderer/src/components/MessageRow.tsx +++ b/src/renderer/src/components/MessageRow.tsx @@ -260,10 +260,14 @@ export function MessageRow({ ] as MenuEntry[]) : []), { label: 'Reply', icon: 'reply', onClick: () => reply() }, - // Never hearing from them again, on every client this account uses - - // Matrix keeps the list on the account rather than in one window, which - // is the difference between blocking somebody and hiding them here. - ...(service === 'matrix' && !isSystem && !message.isOwn && message.senderId + // Never hearing from them again. What that costs differs by service and + // the daemon says which: Matrix keeps the list on the homeserver and + // Discord has a real block, so both hold on every client that account is + // signed in to; IRC, Kick and Sneedchat have nobody to tell, so it is + // this window's own refusal to show what still arrives. The entry is the + // same either way, because the person pressing it is asking the same + // thing - the toast is where the difference is said. + ...(!isSystem && !message.isOwn && message.from ? ([ { label: `Ignore ${message.from}`, @@ -271,7 +275,10 @@ export function MessageRow({ danger: true, onClick: () => { const account = store.accountFor(bufferId) - if (account) store.setIgnored(account.id, message.senderId!, true) + // The protocol id where there is one, since a display name can + // be changed and reused; the name where there is not, which is + // all IRC ever has. + if (account) store.setIgnored(account.id, message.senderId || message.from, true) } } ] as MenuEntry[]) diff --git a/src/renderer/src/components/NickList.tsx b/src/renderer/src/components/NickList.tsx index 761e8c7..8c5c5d7 100644 --- a/src/renderer/src/components/NickList.tsx +++ b/src/renderer/src/components/NickList.tsx @@ -256,6 +256,13 @@ export function NickList(): JSX.Element { .catch((e: Error) => store.toast('error', e.message)) }} onToggleBlock={toggleBlocked} + onIgnore={(ignored) => { + if (!account) return + // The protocol id where the service has one - a display + // name can be changed and reused, and on IRC it is all + // there is. + store.setIgnored(account.id, member.userId || member.nick, ignored) + }} onMention={() => store.startReply('', member.nick, '')} onModerate={(action) => { if (!buffer || !account) return @@ -390,6 +397,8 @@ interface MemberRowProps { /** Which service this row belongs to, so an action is named as it acts. */ service: string | undefined onToggleBlock: (key: string) => void + /** Tells the service, where the service has anything to be told. */ + onIgnore: (ignored: boolean) => void onMention: () => void onModerate: (action: ModerationAction) => void /** Gives this member a role, or takes it away. Discord only. */ @@ -417,6 +426,7 @@ function MemberRow({ service, onSetPower, onToggleBlock, + onIgnore, onMention, onModerate, onSetRole, @@ -442,10 +452,21 @@ function MemberRow({ ...(canWhisper ? ([{ label: 'Whisper', icon: 'lock', onClick: onWhisper }] as MenuEntry[]) : []), ...(canSendFile ? ([{ label: 'Send a file', icon: 'upload_file', onClick: onSendFile }] as MenuEntry[]) : []), { separator: true }, + // Not hearing from them. This used to be a list in this window's own + // preferences, which hid the lines and nothing else: they still counted + // as unread, still notified, still went into the log. It goes to the + // daemon now, which tells Matrix and Discord - both of which have a real + // list of their own - and keeps one itself for the services that have + // none. The window-local list stays only as the thing that hides what is + // already on screen. { - label: blocked ? 'Unblock' : 'Block', + label: blocked ? `Stop ignoring ${member.nick}` : `Ignore ${member.nick}`, icon: blocked ? 'check_circle' : 'block', - onClick: () => onToggleBlock(blockKey) + danger: !blocked, + onClick: () => { + onToggleBlock(blockKey) + onIgnore(!blocked) + } }, ...(perms.canMute ? ([{ label: 'Mute', icon: 'volume_off', onClick: () => onModerate('mute') }] as MenuEntry[]) diff --git a/src/renderer/src/state/store.ts b/src/renderer/src/state/store.ts index c75076a..51da959 100644 --- a/src/renderer/src/state/store.ts +++ b/src/renderer/src/state/store.ts @@ -1667,10 +1667,17 @@ export class ChatStore { * travels to every client signed in, which is what blocking somebody * means, and it agrees with Element. */ - setIgnored(accountId: string, userId: string, ignored: boolean): void { + setIgnored(accountId: string, target: string, ignored: boolean): void { void window.moho - .rpc('setMatrixIgnored', { accountId, userId, ignored }) - .then(() => this.toast('info', ignored ? `Ignoring ${userId}` : `No longer ignoring ${userId}`)) + .rpc<{ scope?: string }>('setIgnored', { accountId, target, ignored }) + .then((answer) => { + // Where it holds is the part worth saying. On Matrix and Discord the + // service itself is told, so it holds everywhere that account is + // signed in; on IRC, Kick and Sneedchat there is nobody to tell, and + // this is moho refusing to show what still arrives. + const scope = answer?.scope === 'account' ? '' : ' here' + this.toast('info', ignored ? `Ignoring ${target}${scope}` : `No longer ignoring ${target}`) + }) .catch((e: Error) => this.toast('error', e.message)) }