Commit Graph
168 Commits
Author SHA1 Message Date
ancsemi fe5ed5b8a0 Replace em dashes in client code comments and styles 2026-10-10 16:08:55 -04:00
ancsemi 0b8c04e708 Replace em dashes in the English UI text and messages 2026-10-10 16:08:54 -04:00
ancsemi d9591eba7e Let admins make new accounts wait before posting (#5742)
A new Auto-Mod setting, "New accounts must wait before posting (minutes)",
sits next to the new-account link rule. 0 (the default) turns it off; the
maximum is 10080 minutes (one week).

While an account is younger than that, the server refuses channel messages,
forum topics, attachments, thread replies, polls, scheduled messages and DMs
to other people. A DM with yourself is allowed. Admins and members at or above
the Auto-Mod exempt role level are not held back. If the check itself fails,
the post is refused rather than let through.

The refusal says how long is left, and the app puts the message (or the
forum post being written) back so nothing is lost.
2026-10-09 11:24:39 -04:00
Amnibro 28f5f0f6b8 Group DMs: create, invite, leave, and end-to-end encrypted messaging in the web client (#5733) 2026-10-05 17:59:39 -04:00
ancsemi 7c180e94f3 Settings survive a shaky first connection: preferences are asked for again, channel icons redraw (#5723) 2026-10-05 17:25:57 -04:00
ancsemi 4218d041c2 Self-destructing messages: a refused send comes back, cache busters (#5725)
A self-destructing message the server refused came back (if at all) with
the timer off, so a resend went out permanent. Too long, it now returns
with the timer on; refused for permission, the text returns so it can be
sent normally by choice. Cancelling the dialog puts focus back in the
message box, and the unit buttons say which is chosen. The cache busters
for every file the feature touched are bumped, since a stale module would
have thrown on channel switch.
2026-10-03 23:20:30 -04:00
Bernardo Costa c3ade67336 Screen sharing: drop the native GStreamer path, add a bitrate setting, fix black tiles on GPU encoders (#5672)
The native GStreamer screen share path is gone: in a peer-to-peer mesh every
hardware and OS combination behaved differently, and keeping them in step cost
more than it saved. Screen sharing uses Chromium's own capture everywhere.

- Screen share bitrate is a setting: a stepper from 300 to 10,000 Kbps or
  unlimited, with press-and-hold repeat and typed entry. Changes apply live,
  including to a share going through the voice relay (debounced, so a hold
  becomes one update). The default stays 1080p at 30 fps with an 8,000 Kbps cap.
- Black screen share tiles on GPU encoders: the sharer asks for a fresh
  keyframe after negotiation, and the watchdog only re-adopts tracks a tile is
  not already showing.
- Relay screen publishing is serialized and tied to the share it was started
  for, so a stop, a new share or a relay restart can never publish a stale
  producer, and a failed first publish retries on its own.
2026-10-02 12:12:31 -04:00
ancsemi 01665eb262 Split app-socket.js (2,844 lines) into three files, a pure move
_setupSocketListeners was one 2,602-line function. Its statements are now six
_listen methods called in the original order, so every listener is registered
in the same order as before; no local variable crosses from one part to
another. The parts live in app-socket.js (session, connect and wake),
app-socket-channels.js (channels, messages, members, voice rosters) and
app-socket-events.js (everything else the server pushes).

Checked: all 1,124 other methods have identical source, the six parts rebuild
the old function exactly, and in a running app every one of the 108 events
the old function listened for still has its listener. Messages, edits,
deletes, reactions, pins, settings and reconnecting were exercised live.
2026-10-02 11:23:12 -04:00
ancsemi 1655546937 Browser code: every silent catch now logs or says why it is safe
All 437 empty catch blocks in public/js were reviewed. Failures worth knowing
about (desktop bridge calls, native screen share cleanup, saved output device
routing, DM and thread render steps, resync steps, server list sync, settings
and library loads, offer rollbacks) now print a tagged console warning.
Expected failures (blocked storage in private mode, autoplay and fullscreen
refusals, connections that are already closed, malformed saved values) carry a
one-line reason. A few try blocks around code that cannot throw were removed.
Behaviour is otherwise unchanged.
2026-10-02 09:25:12 -04:00
michues 79da21ad53 feat: configure auto-away behavior (#5705) 2026-09-29 17:59:05 -04:00
ancsemi 790658ec21 Member lists scale: updates are batched per channel, the app gets only the members that changed with default fields left out, and older apps still get whole lists; 200 people arriving went from 700 MB of traffic to 10 MB 2026-09-27 02:34:24 -04:00
Amnibro f9a3aa9c5b DM calls: starting voice in a DM or group DM rings the other members (#5697) 2026-09-26 16:31:58 -04:00
ancsemi 1e6039c0fa The open chat notifies when Haven Desktop is minimised or behind other windows, on Desktop versions that hand focus back to the page (Haven-Desktop #58) 2026-09-25 21:33:07 -04:00
ancsemi 73285c0d9d Settings > Encryption: lock the encrypted-DM key backup with a passphrase of your own instead of your login password, which the server receives at every sign-in; sign-in then asks for the passphrase, and a password change leaves the backup alone 2026-09-24 00:25:52 -04:00
ancsemi 8160d7056b A thread reply refused as too long comes back to the thread box instead of vanishing (#5691) 2026-09-23 23:33:48 -04:00
ancsemi 115820db31 A message refused as too long comes back into the box, and an encrypted DM near the limit is no longer refused for the length encryption adds (#5691) 2026-09-23 19:34:32 -04:00
ancsemi 0e24e23df5 Delete All My Messages, behind an admin switch under Members: every message and file a person posted goes in one go, protected ones stay (#5686) 2026-09-21 21:33:38 -04:00
Amnibro 20640e94cd Add a Channel Functions switch that turns off message reactions. (#5677)
Off hides the react control and the server refuses add or remove. Existing badges stay. DMs stay on.
2026-09-18 19:56:06 -04:00
Mattandbo0sted 4a3080c92c [big feat] Attachment tagging (#5678)
* Optional tags on uploads, plus tag: search and clickable tags

Attach an optional tag to any image or file before sending it, from a new bar under the composer previews. Up to three tags per attachment. Clicking a queued attachment switches the bar to that attachment's tags. Applying an existing tag is open to anyone who can upload; creating a new one needs the new Manage Tags permission. Tags are one global vocabulary, kept separate from the forum topic tags.

Messages that carry tagged attachments show a Tags footer listing every tag, and each chip is clickable to search for that tag.

The search bar gains a tag: token with a dropdown picker of existing tags. The match is a loose prefix, so tag:do finds dog, dogs and doghouse. When what you type matches no current tag, a Search removed tags button runs the search anyway, so a tag that gets removed later still finds its old attachments.

New tables upload_tags and attachment_tags store the vocabulary and the links.

* Retroactive tag editing, gallery tag chips, filter, bulk manage

Phase 3a: edit tags on already-sent attachments from the message context menu, manage_tags-gated for others' messages, with live footer updates.

Phase 3b: show tags in the Files & Media gallery, filter by tag (AND, exact match), and bulk append or replace tags across selected attachments from select mode. Replacing with no tags clears them behind a second confirmation.

Photo and video tiles render their tag chips in a bottom overlay so the tile's aspect-ratio and overflow:hidden no longer clip them, and the toolbar buttons are sized to match the Sort and Size selects.

* Admin-configurable tag limits and a Tags management panel

Adds the max tags per attachment and max tag length as server settings,
surfaced in the Limits admin section and clamped to the same ceilings on
both sides. The tag apply, edit and bulk paths read the live limits.

Adds a Tags admin section (manage_tags) to add, rename and delete
vocabulary entries. Rename and delete are hard and propagate to every
attachment through the association's tag_id, so both go behind a
destructive confirm. A rename that collides with an existing tag merges
onto it. A shared helper collects the affected messages and rebroadcasts
their footers so open views update live.

Also groups the media gallery toolbar so the Tags filter button sits with
Sort instead of floating in the middle of the row.

* Move Manage tags beside the Tags filter and scope gallery Select all to the tag filter

Relocate the gallery's Manage tags control out of the select-mode action
group (where it sat directly beside Delete selected) to next to the Tags
filter button, so a tag action and the destructive delete are no longer
adjacent. Visibility is still driven by the same _refreshMediaGalleryToolbar
logic, so it only appears in select mode for users with manage_tags.

Fix Select all so it only selects the items visible under the active tag
filter, reusing _filterMediaItemsByTags, instead of every item in the tab.
Previously it selected attachments hidden by the filter, which made a
follow-up bulk action apply far more widely than the visible selection.

---------

Co-authored-by: bo0sted <mc526+github@proton.me>
2026-09-18 19:55:56 -04:00
ancsemi 34eb24eaed Forum topics open across the whole chat column with a title bar and the full first post; a Side panel button switches back (#5659) 2026-09-15 18:37:35 -04:00
Matt 1c9a8d555f Saved timezone and 12/24 hour clock, applied to every timestamp (#5667) 2026-09-15 17:46:08 -04:00
ancsemi d2a014df7a One-time admin notice that channel access moved to Required roles (#5649) 2026-09-12 20:55:55 -04:00
ancsemi f07cab7af0 Send later: schedule a message for a time you pick (#5638) 2026-09-12 18:07:00 -04:00
ancsemi 4ec542c0c7 Admins can set the layout everyone opens a forum in (#5656) 2026-09-12 17:25:21 -04:00
ancsemi 6c645f26c1 Quotes need a space after >, a switch to hide Send, and no GIF button without a provider (#5654) 2026-09-12 00:56:29 -04:00
ancsemi 4f084f047b Edit a posted role menu: roles, emojis and text (#5644) 2026-09-12 00:56:28 -04:00
ancsemi e6b472e7e0 Forum cards get their own right-click menu, and the thumbnail opens the topic (#5650, #5646) 2026-09-12 00:56:27 -04:00
ancsemi 912ec8c42a Forum topics carry a per-account unread dot, with Mark all read (#5641) 2026-09-11 00:22:48 -04:00
ancsemi 899a5fc1be Muted users can no longer edit messages or react, and anyone who can mute can unmute (#5640) 2026-09-10 23:11:26 -04:00
ancsemi a5bb33b399 Admin setting to hide the crossed-out feature icons in the channel list (#5615)
On a server where most channels have screen share and music turned off, the two crossed-out icons on every channel were clutter that also truncated names. A server-wide setting under the admin options hides them for everyone; the slow mode and cleanup badges stay.
2026-09-09 21:26:08 -04:00
Amnibroandancsemi 52c7dbe6e5 Role templates, required roles (any/all), per-role upload caps, channel templates, self-assign role menus (#5597)
Five items from Dispencer2's wishlist, on top of the bug PRs.

- Role Management: New Role opens a template picker (Moderator, Helper,
  Trusted member, Event host, Media poster, Group, Blank), each with a
  level, colour and permission set ready to edit.
- Channel Functions: Required roles row with an Any / All switch. The gate
  sits on top of membership: getEnrichedChannels, get-messages and
  send-message all check it, losing a role leaves the room live, admins are
  exempt, and delete-role strips the role from every gate.
- roles.max_upload_mb: a role can raise the upload cap for its holders. The
  upload routes, the disk guard margin and the composer's pre-check all read
  the same effective cap; get-server-settings reports it per user as
  max_upload_mb_effective and a role change pushes the new value.
- Create Channel: template picker (built-ins plus templates saved from any
  channel through Channel Functions). create-channel accepts topic, readOnly,
  announcement, slowMode, mediaEnabled and voiceEnabled so a template lands
  in one trip.
- Role menus: create-role-menu posts a message with one emoji per role and
  seeds the reaction chips; toggle-self-role and the add/remove-reaction
  handlers grant or drop the role, and every reader gets their own held list.

test/rolesWishlist.test.js drives all of it against a real server.

Co-authored-by: ancsemi <ancsemi@gmail.com>
2026-09-09 21:17:33 -04:00
ancsemi c2011c91fa DM PiP header shows the partner's avatar and live status (#5574, #5600)
The header read presence from the online list of whichever channel was on screen, so a partner outside that channel showed as away with an initial until the DM was opened full screen. The DM target now carries avatar fields, the client keeps every channel's online list and reads the DM's own, asks for it when the PiP opens, and the server refreshes DM rooms whenever a member connects, disconnects for good, or changes status.
2026-09-09 19:37:42 -04:00
ancsemi c8cd814023 Forum topic cards carry the protection shield (#5622)
A protected topic showed no shield on its card and, with no archived flag on the element, the context menu offered Protect again instead of Unprotect. The card now sets the flag and shows the shield, the protect and unprotect events update the card live, and the cached topic keeps the state across re-renders.
2026-09-09 19:22:33 -04:00
ancsemi 8a54f04ca7 Android banner stays dismissed once closed or opted out of (#5594)
Closing the top-bar banner, or ticking Don't show this again on the Android promo, now keeps it gone on that account across reloads and devices. The permanent flag it checked was never written anywhere, so it returned on every page load.
2026-09-08 23:07:23 -04:00
Amnibro 790659db3c Forums read like forums: topic cards, gallery, sort, tags, New Post, plus NSFW channels, settings search and one create form (#5595)
A forum channel now opens as a list of topic cards, newest activity on top, each with its title, tags, author, reply count and first image, and a Gallery view of square tiles. The toolbar sorts by Recently active or Date posted, filters by the channel's tags (match some or all), and a New Post button opens a composer with title, body and tags. Admins keep the tag list in Channel Functions, and the author or a moderator can retitle or retag a topic from its card. Also in: an NSFW switch per channel with a Hide NSFW channels setting, a search box inside Settings (Ctrl+F), Create Temp Channel opening the normal create form with Temporary ticked, and & and + allowed in channel names. Requested by test2 and Dispencer2.
2026-09-08 17:57:56 -04:00
ancsemi 9a78bfb7c1 Restored effects apply when no theme pass follows (#5589)
The preferences handler restored the effects pick and relied on the theme pass to apply it. With no personal theme and no server default theme there is no theme pass, so the pick sat in storage until the next reload.
2026-09-08 13:24:04 -04:00
Amnibro 4b5155d4b8 Visual effects survive a desktop relaunch, synced like the theme (#5589)
The effects pick lived only in localStorage, so a desktop launch that landed on a different storage origin came back with the theme's default effects while the theme itself survived through the server. Every picker write now mirrors to user_preferences like the theme does, and the preferences handler restores it before the theme is applied. Reported by Dispencer2.
2026-09-08 13:21:52 -04:00
ancsemi 8cd906c75d Forum feeds run newest first: active topics on top, older ones load from the bottom (#144) 2026-09-07 19:50:25 -04:00
ancsemi c75141047a Role mentions: @Role pings everyone holding the role, behind the mention_everyone permission, with a per-user switch (#5579) 2026-09-07 19:24:49 -04:00
ancsemi 8fd4b07bcb Server-stored nicknames are the record; stop re-pushing cached ones on connect (#5560) 2026-09-07 13:01:04 -04:00
Matt 2b9dc381a4 Promo dismissals live on the account, and login no longer re-asks for consent (#5570)
The desktop and mobile promo popups and the recovery-codes notice remember Don't show again on the account instead of in localStorage, so hardened browsers that wipe storage stop re-showing them. The 18+ and Terms checkboxes now appear only when creating an account; login carries a note that signing in accepts the current Terms. Thanks to @Bo0sted.
2026-09-05 16:35:08 -04:00
Bernardo Costa 0c31d06598 Negotiated native screen sharing transport, off by default (#5555)
Server-side signaling and codec negotiation for a native Desktop screen share path. Off unless the user opts in from Settings, and the toggle only appears inside Haven Desktop builds that carry the native helper. Browser and per-app audio capture are unchanged. Thanks to @bernardokcosta.
2026-09-05 16:34:57 -04:00
ancsemi 4614e8da1b Add forum channels (#144)
A forum is a regular channel where every top-level message is a topic.
Replies go in the message's thread, the topic shows its reply count, and a
reply bumps the topic to the newest end of the channel, so old topics
resurface instead of sinking. Toggle it in Channel Functions or tick Forum
when creating a channel.

The history query orders topics by their latest activity, with cursors
resolved to activity stamps so paging keeps working, and topics never
compact into each other so a bump can move the node in place. Covered by an
integration test that boots a scratch server.
2026-09-03 00:33:30 -04:00
ancsemi 0db4c232bf Keep the hover profile card and gear buttons alongside the unified user menu
PR #5554 folded the moderation actions into the right-click menu, which stays.
This restores the hover preview card behind a new Settings > Chat toggle (on by
default) and brings back the gear buttons on member rows and the profile card.
Both gears now open that same unified menu anchored to the button, so touch and
keyboard users keep a visible way into the moderation actions.
2026-09-03 00:00:50 -04:00
Mattandbo0sted 0b651e9b31 Unified user context menu and profile card redesign (#5554)
* Remove hover profile card, copy its styling to the click card

* Anchor the profile card to the right of the clicked name or icon

* Toggle the profile card when the same user is clicked

* Unify the user menus into one right-click context menu

Fold the mod-only gear menu into the member-list context menu so
right-clicking a user (member list or chat author) opens one menu with
everything: profile actions plus role/channel/moderation, gated as
before. Remove all gear-icon entry points (profile card and sidebar),
the old gear menu, and the profile card's Set Nickname button, since the
context menu now covers them. Add a "Role Management" label across locales.

* Open the full-res avatar in the lightbox from the profile card

Clicking a real avatar (not the letter fallback) on a profile card opens
the image in the existing lightbox, for other users and yourself. The
card stays open behind it: the lightbox now sits above the card, and the
card's outside-click handler ignores clicks on the lightbox so dismissing
the image keeps the card open.

---------

Co-authored-by: bo0sted <mc526+github@proton.me>
2026-09-02 23:51:31 -04:00
Bernardo Costa 5f4c8ccd44 Internationalize the full client interface (#5551) 2026-09-01 21:55:56 -04:00
Matt 52c68bac2a Add a generic listening-presence webhook and a live profile card (#5523)
* Add Navidrome rich presence integration

Shows what a user is playing in Navidrome on their Haven profile, through the
Havidrome companion plugin posting to a reserved per-user webhook.

Users generate a revocable webhook token in Settings > Activity > Navidrome.
The webhook takes strict multipart: title and duration are required, and cover
bytes are magic byte checked and held in Haven's memory. The Haven server hands
those bytes to viewers' browsers from its own /api/activity/navidrome-cover
path, so Navidrome is never contacted and its address never leaves the plugin.
Presence runs through the existing activity slot so it stays online gated,
clears on disconnect, and auto expires at track end. The profile card gains a
live progress bar; pause freezes it, stop clears it.

* Update the open profile card live from presence

The profile card was a snapshot, so a track pausing, resuming, changing or
ending did nothing until it was reopened. It now re-renders its activity from
the online-users broadcasts the client already receives, so the bar freezes on
pause, resumes on play, swaps on a new track and clears on stop while the card
stays open. It rides the existing presence stream, no new traffic.

* Make the whole profile card live and stop it clipping off-screen

The open profile card only re-rendered its music activity from presence;
status (online/away/dnd/offline) and custom status text stayed frozen at
whatever they were when it opened. It now refreshes all three from the
online-users broadcasts the client already receives, so a user going away,
setting a status or dropping offline updates the card in place while it
stays open. Shared _statusDotClass/_statusLabel/_profileStatusTextHtml
helpers keep the initial render and the live refresh in sync, and the
custom-status line gets a stable slot so it can appear and clear. Rides
the existing presence stream, no new traffic.

Positioning measured the card with a hardcoded 320x400 guess, but its
width is a rem value and its height varies with content, so once font-size
or zoom differed from 16px the real card was larger than the guess and the
viewport clamp let it spill off the right edge (desktop app and web). It
now measures the real offsetWidth/offsetHeight before clamping.

* Expire a paused Navidrome track after 5 minutes idle

A paused track cleared its play-to-end timer and set no replacement, so
the slot lingered indefinitely with no further posts. Since presence is
gated on being online, the stale track stayed hidden while offline but
reappeared on reconnect. Arm a 5-minute idle timeout while paused instead;
every update resets it, so an actively-paused client stays shown.

* Rate-limit the Navidrome cover route

The cover-serving GET had no limiter while the webhook it pairs with did.
It's viewer-facing (every open profile card fetches it), so add a separate,
looser cap (120/min vs the webhook's 60/min) rather than reusing the
webhook limiter, keeping the two routes consistent without throttling
legitimate viewing.

* Make the music presence webhook generic, not Navidrome-only

The rich presence webhook was tied to Navidrome and a single companion
plugin, so Haven had a settings page that only worked with one source and
would point at nothing if that plugin went away.

It is now a source-agnostic Listening webhook. Any music player that can
run a plugin or a script posts a title, artist and duration (plus optional
album, position, cover art and a source label) to a personal URL. The
contract is documented in listening_api.md.

- Route is /api/webhooks/listening/:token, cover is
  /api/activity/listening-cover/:userId. Table navidrome_tokens is renamed
  to listening_tokens with a migration that keeps existing tokens.
- A track is identified by a server-side hash of title, artist and
  duration rather than a poster-supplied id, so nothing untrusted is used
  as an identifier.
- Expiry is a capped lease instead of trusting the reported duration: a
  playing track is held until it would end, up to 60 minutes, and long
  media stays visible by sending a heartbeat. A fake duration can no
  longer leave a status stuck.

Anyone still using the old Navidrome webhook needs to point their plugin
at the new /api/webhooks/listening URL.

* Scroll long track titles and artists instead of clipping them

A long title or artist was cut off with an ellipsis at whatever width
the viewer's profile card happened to be, so different people saw
different amounts of the same track. Past 30 characters the text now
scrolls in a seamless loop at a constant speed, gated on character
count rather than pixel width so it reads the same for everyone.
Shorter text is unchanged, and the scroll respects reduced-motion.

* Trim the redundant intro in the listening API doc
2026-09-01 20:50:37 -04:00
ancsemi 0f2bfa66d4 Let invite_users holders actually open the Admin tab (#5470)
The invite_users permission unhid the Invite Code nav item, but the Admin tab
switch and the admin panel container each kept their own permission lists,
and neither included it. Holders saw an Admin tab that did nothing when
clicked, and the section stayed display:none behind it.

All three places now share one gate, _hasAnyAdminSettingsAccess, so a
permission cannot be added to one list and missed in the others. The rename
handler uses it too, which stops a moderator from losing the Admin tab after
changing their display name.
2026-09-01 19:35:37 -04:00
birdcrazy b5870bfde5 Add self-serve Groups via level 0 roles (#5548)
* Allow level 0 role levels to be created
Also change the text where appropriate to indicate level 0 roles can be created.

* Allow roles to also be edited to be level 0.

* let server allow role updates to level 0 be saved

* fix non-english locals litterally saying "self-serve" in english

* Enforce permission removal server-side on create-role and update-role

* Fix update-role audit log values

The values sent to the audit log were sending True for parameters that were supplied. It wasn't checking if something actually changed.
Fixed to properly set the values based on what changed.

Signed-off-by: birdcrazy <227081272+birdcrazy@users.noreply.github.com>

* Add spacing between audit-detail-pair

There was no space between values and the following key in the audit log

Signed-off-by: birdcrazy <227081272+birdcrazy@users.noreply.github.com>

* Fix lvl 0 assignment reporting the previous roles in audit log

Signed-off-by: birdcrazy <227081272+birdcrazy@users.noreply.github.com>

* Hide permissions list role editors when role lvl=0

* Change wording for level_0_role_note

* Role list refinement
Add level indicators to roles in the role list.
Add divider between level-0 roles and leveled roles.
Add "Groups" lable above level-0 roles.

* Change role sidebar level display to match the style of the channel role manager

* fix html class defined with a .

* Add "groups" divider to channel roles modal.

* fix translation pointing to wrong key

* Add groups to profile settings.
list user's current groups.

Group assignment still needs to be added.

* add missing translation key, change groups list style

* Hide Groups section if no lvl 0 roles are available
Add group-manager so user can select groups to join
as well as a save button and a socket to allow user to apply lvl 0 roles to themselves.

* bug fix: groups section doesn't display because roles arn't loaded.

* add style to groups manager, and fix bug where saving rename causes user's groups to disapear until hard refresh

* Add methods to allow user to see which channels each "Group" grants.
Using temporary tooltip for now.

* First attempt at a popup containing the channel grants

* channel access refinements
keep within window bounds.
only list sub-channels if the group grants the parent, or the user already has access to the parent.

* remove owner parent channel check as that is overcomplicating things for now.

* Add translations for group channel access popup

* bug fix: two groups both grant the same channel, user removes one, and adds the other at the same time, the channel would be removed.

* Also save groups on save-rename-btn click

---------

Signed-off-by: birdcrazy <227081272+birdcrazy@users.noreply.github.com>
2026-09-01 19:28:16 -04:00
Bernardo Costa c68674f789 Add theme compatibility and recovery safeguards (#5545) 2026-09-01 00:59:54 -04:00