Reduce status to Online and Idle, and stop tracking the packed tarball

Do Not Disturb is gone from the picker, along with what it needed: the
notifier's account-status tracking and main's watching of the account list,
which existed only to enforce it with the window closed. Status is now
purely per account, so choosing one no longer reaches across the others.

moho-0.1.0.tgz was committed - 19.5MB of npm pack output, a build artifact
regenerable at any time. Untracked, and *.tgz ignored so the next `npm
pack` does not put it back.

The .presence-dot.dnd style stays: that renders other people's Discord
status in the friends list, where DND is a value they really can have.
This commit is contained in:
2026-08-22 23:17:28 -04:00
parent fa59525739
commit cbc4ddb2d0
7 changed files with 13 additions and 49 deletions
+2
View File
@@ -6,6 +6,8 @@
node_modules/
/out/
/dist/
# npm pack output - a build artifact, regenerable with `npm pack`
*.tgz
*.log
*.tsbuildinfo
+1 -1
Submodule nobilis updated: 94e801f024...e6b8244627
+1 -9
View File
@@ -221,15 +221,7 @@ function applyHotkey(accelerator: string): void {
function wireIpc(): void {
ipcMain.handle(IPC.rpc, async (_e, method: string, params: Record<string, unknown>) => {
try {
const result = await client.request(method, params)
// Main needs each account's status to enforce Do Not Disturb, and the
// renderer already asks for the account list whenever it changes -
// watching the answer keeps the two in step without a second channel or
// a poll of its own.
if (method === 'listAccounts' && Array.isArray(result)) {
notifier.trackAccounts(result as { id: string; status?: string }[])
}
return { ok: true, result }
return { ok: true, result: await client.request(method, params) }
} catch (err) {
// Surfaced as a value rather than a rejection so the renderer sees
// nobilis's own error text (which is often the actionable part - "account
-13
View File
@@ -35,8 +35,6 @@ export class Notifier {
/** Source path -> transcoded PNG, so a repeat sender is converted once. */
private transcoded = new Map<string, string>()
/** Accounts currently set to Do Not Disturb. */
private dndAccounts = new Set<string>()
constructor(
private prefs: Prefs,
@@ -46,11 +44,6 @@ export class Notifier {
private renderer: () => WebContents | null = () => null
) {}
/** Mirrors each account's status, so DND can be enforced here. */
trackAccounts(accounts: { id: string; status?: string }[]): void {
this.dndAccounts = new Set(accounts.filter((a) => a.status === 'dnd').map((a) => a.id))
}
trackBuffer(buffer: ChatBuffer, removed: boolean): void {
if (removed) this.buffers.delete(buffer.id)
else this.buffers.set(buffer.id, buffer)
@@ -67,12 +60,6 @@ export class Notifier {
* all; see isMuted for why.
*/
private isMuted(accountId: string, bufferId: string): boolean {
// Do Not Disturb is a statement about the person rather than any one
// connection, so it silences every account regardless of which one it was
// set from. Held in main so it keeps working with the window closed,
// which is the whole point of the setting.
if (this.dndAccounts.size > 0) return true
const muted = this.prefs.get<string[]>('mutedBuffers', [])
if (muted.includes(bufferId)) return true
+7 -24
View File
@@ -1,6 +1,6 @@
import { ContextMenu, useContextMenu } from './ContextMenu'
import { Icon, MaskIcon } from './Icon'
import { useChat, useStore } from '../state/hooks'
import { useStore } from '../state/hooks'
import { nickColor, resolveMediaUrl, serviceIcon } from '../lib/util'
import type { Account } from '../../../shared/wire'
@@ -9,33 +9,22 @@ import type { Account } from '../../../shared/wire'
* channel list - and the way to change how you are presenting.
*
* Per account rather than global because that is what the underlying status
* is: each protocol carries its own, and only Do Not Disturb reaches across
* all of them (by silencing notifications, which is this app's own doing
* rather than anything a server is told).
* is: each protocol carries its own.
*/
export type Status = 'online' | 'idle' | 'dnd'
export type Status = 'online' | 'idle'
const STATUSES: { id: Status; label: string; glyph: string; note?: string }[] = [
const STATUSES: { id: Status; label: string; glyph: string }[] = [
{ id: 'online', label: 'Online', glyph: 'circle' },
{ id: 'idle', label: 'Idle', glyph: 'dark_mode' },
{
id: 'dnd',
label: 'Do Not Disturb',
glyph: 'do_not_disturb_on',
note: 'Silences notifications everywhere'
}
{ id: 'idle', label: 'Idle', glyph: 'dark_mode' }
]
export function statusColor(status: string): string {
if (status === 'idle') return 'var(--warning)'
if (status === 'dnd') return 'var(--error)'
return 'var(--success)'
return status === 'idle' ? 'var(--warning)' : 'var(--success)'
}
export function UserFooter({ account }: { account?: Account }): JSX.Element {
const store = useStore()
const accounts = useChat((s) => s.accounts)
const { menu, open, close } = useContextMenu()
// With no account selected there is nobody to show, but the footer still
@@ -46,13 +35,7 @@ export function UserFooter({ account }: { account?: Account }): JSX.Element {
const service = serviceIcon(account.service)
const name = account.displayName || account.id
const setStatus = (next: Status): void => {
// DND is a statement about the person, not the connection, so it goes to
// every account at once. The others are per-account, matching where the
// user set them from.
const targets = next === 'dnd' || status === 'dnd' ? accounts : [account]
for (const a of targets) void store.setAccountStatus(a.id, next)
}
const setStatus = (next: Status): void => void store.setAccountStatus(account.id, next)
return (
<>
+1 -1
View File
@@ -256,7 +256,7 @@ export class ChatStore {
* disconnected account or a protocol with no presence concept the answer is
* no - showing it as set anyway would be a lie.
*/
async setAccountStatus(accountId: string, status: 'online' | 'idle' | 'dnd'): Promise<void> {
async setAccountStatus(accountId: string, status: 'online' | 'idle'): Promise<void> {
try {
await window.moho.rpc('setAccountStatus', { accountId, status })
await this.refreshAccounts()
+1 -1
View File
@@ -6,7 +6,7 @@ export interface Account {
service: 'irc' | 'discord' | 'sockchat' | 'matrix' | 'jabber' | 'slack'
displayName: string
/**
* How the user is presenting: "online" | "idle" | "dnd". Distinct from
* How the user is presenting: "online" | "idle". Distinct from
* `state`, which is whether the connection is up.
*/
status?: string