Ignore somebody on the account, not just in this window
Right-clicking a Matrix message offers to ignore its sender, which writes the account's own list - so it holds on every client you sign in from and agrees with Element, unlike the local block it sits beside. The account panel lists who is ignored with a way back, because the person you want to un-ignore is by definition somebody whose messages you can no longer see. Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
+1
-1
Submodule nobilis updated: 93f2d5c571...ffa406a2ad
@@ -32,6 +32,7 @@ interface CrossSigningStatus {
|
||||
export function MatrixAccountTools({ account }: { account: Account }): JSX.Element {
|
||||
const store = useStore()
|
||||
const verification = useChat((s) => s.matrixVerification)
|
||||
const ignored = useChat((s) => s.matrixIgnored)[account.id] || []
|
||||
const [devices, setDevices] = useState<MatrixDevice[] | null>(null)
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [recoveryKey, setRecoveryKey] = useState('')
|
||||
@@ -52,6 +53,10 @@ export function MatrixAccountTools({ account }: { account: Account }): JSX.Eleme
|
||||
.then(setDevices)
|
||||
.catch((e: Error) => store.toast('error', e.message))
|
||||
.finally(() => setLoading(false))
|
||||
void window.moho
|
||||
.rpc<{ users: string[] }>('listMatrixIgnored', { accountId: account.id })
|
||||
.then((answer) => store.noteIgnored(account.id, answer.users))
|
||||
.catch(() => {})
|
||||
void window.moho
|
||||
.rpc<CrossSigningStatus>('matrixCrossSigningStatus', { accountId: account.id })
|
||||
.then(setCrossSigning)
|
||||
@@ -173,6 +178,36 @@ export function MatrixAccountTools({ account }: { account: Account }): JSX.Eleme
|
||||
</p>
|
||||
)}
|
||||
|
||||
{/* Who this account has asked never to hear from, and the way back.
|
||||
Listed here rather than only offered from a message, because the
|
||||
person you want to un-ignore is by definition somebody whose
|
||||
messages you can no longer see. */}
|
||||
{ignored.length > 0 && (
|
||||
<div className="setting-row">
|
||||
<div className="setting-text">
|
||||
<div>Ignored</div>
|
||||
<div className="small muted">
|
||||
Kept on the account, so it holds on every client you sign in from.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{ignored.map((userId) => (
|
||||
<div key={userId} className="device-row">
|
||||
<Icon name="block" size={18} color="var(--warning)" />
|
||||
<div className="setting-text">
|
||||
<div className="ellipsis">{userId}</div>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
className="button subtle"
|
||||
onClick={() => store.setIgnored(account.id, userId, false)}
|
||||
>
|
||||
Stop ignoring
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
|
||||
{loading && <p className="small muted">Loading sessions…</p>}
|
||||
{devices?.length === 0 && <p className="small muted">No other sessions signed in.</p>}
|
||||
|
||||
|
||||
@@ -253,6 +253,22 @@ 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
|
||||
? ([
|
||||
{
|
||||
label: `Ignore ${message.from}`,
|
||||
icon: 'block',
|
||||
danger: true,
|
||||
onClick: () => {
|
||||
const account = store.accountFor(bufferId)
|
||||
if (account) store.setIgnored(account.id, message.senderId!, true)
|
||||
}
|
||||
}
|
||||
] as MenuEntry[])
|
||||
: []),
|
||||
// What the room tells everyone to read first. Offered to everybody
|
||||
// rather than gated on a power level read here: the server decides, and
|
||||
// it refuses in words worth showing - hiding the action from somebody who
|
||||
|
||||
@@ -366,6 +366,8 @@ export interface ChatState {
|
||||
reviewCard: LiveCard | null
|
||||
/** Which messages each Matrix room has pinned, newest last. */
|
||||
matrixPinned: Record<string, string[]>
|
||||
/** Who each Matrix account has asked never to hear from. */
|
||||
matrixIgnored: Record<string, string[]>
|
||||
/**
|
||||
* The profile being shown, or null.
|
||||
*
|
||||
@@ -441,6 +443,7 @@ const INITIAL: ChatState = {
|
||||
hiddenPolls: [],
|
||||
reviewCard: null,
|
||||
matrixPinned: {},
|
||||
matrixIgnored: {},
|
||||
profile: null,
|
||||
replyingTo: null,
|
||||
toasts: [],
|
||||
@@ -1440,6 +1443,25 @@ export class ChatStore {
|
||||
this.set({ hiddenPolls: this.state.hiddenPolls.filter((k) => k !== key) })
|
||||
}
|
||||
|
||||
/** Seeds the ignore list from a direct read, before any event arrives. */
|
||||
noteIgnored(accountId: string, users: string[]): void {
|
||||
this.set({ matrixIgnored: { ...this.state.matrixIgnored, [accountId]: users } })
|
||||
}
|
||||
|
||||
/**
|
||||
* Ignores somebody on Matrix, or stops.
|
||||
*
|
||||
* The account's own list rather than this window's `blockedNicks`: it
|
||||
* 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 {
|
||||
void window.moho
|
||||
.rpc('setMatrixIgnored', { accountId, userId, ignored })
|
||||
.then(() => this.toast('info', ignored ? `Ignoring ${userId}` : `No longer ignoring ${userId}`))
|
||||
.catch((e: Error) => this.toast('error', e.message))
|
||||
}
|
||||
|
||||
/**
|
||||
* Pins a message in a Matrix room, or takes the pin off.
|
||||
*
|
||||
@@ -1614,6 +1636,15 @@ export class ChatStore {
|
||||
break
|
||||
}
|
||||
|
||||
case 'matrixIgnored':
|
||||
this.set({
|
||||
matrixIgnored: {
|
||||
...this.state.matrixIgnored,
|
||||
[data.accountId as string]: (data.users as string[]) || []
|
||||
}
|
||||
})
|
||||
break
|
||||
|
||||
case 'matrixPinned':
|
||||
this.set({
|
||||
matrixPinned: {
|
||||
|
||||
Reference in New Issue
Block a user