Implement per-room user UI + speaking #84

Merged
seb merged 8 commits from issue/47-channel-user-info into main 2026-04-16 13:04:34 -07:00
Collaborator

Closes #47.

  • Convert home.tsx into a set of tiles that lists the users connected to the current channel
  • Add a set of sub-buttons underneath the currently-active panel, one per user
  • Add the currently-logged in user to the bottom of the main sidebar (same component as the settings page)
  • Unify the atproto did/name/profile pic logic between the home.tsx tiles, the sidebar sub-buttons, and the logged-in user components
  • Add a connection strength dot + a reactive ActiveSpeakerChanged border to all 3 profile pic locations if connected to a channel
  • Add a button in the bottom-right of the home screen if connected to list connection strength statistics
  • Add endpoints to support the new connection strength/isSpeaking logic
Closes #47. - Convert home.tsx into a set of tiles that lists the users connected to the current channel - Add a set of sub-buttons underneath the currently-active panel, one per user - Add the currently-logged in user to the bottom of the main sidebar (same component as the settings page) - Unify the atproto did/name/profile pic logic between the home.tsx tiles, the sidebar sub-buttons, and the logged-in user components - Add a connection strength dot + a reactive ActiveSpeakerChanged border to all 3 profile pic locations if connected to a channel - Add a button in the bottom-right of the home screen if connected to list connection strength statistics - Add endpoints to support the new connection strength/isSpeaking logic
seb 2026-04-10 14:54:41 -07:00
seb added this to the Laurence milestone 2026-04-10 14:54:57 -07:00
seb self-assigned this 2026-04-10 14:55:00 -07:00
seb force-pushed issue/47-channel-user-info from 5c8fc251f4 to 0390c0927c 2026-04-10 14:55:09 -07:00 Compare
seb force-pushed issue/47-channel-user-info from 380a5e6aba to 8263a0ed28 2026-04-10 15:28:26 -07:00 Compare
seb force-pushed issue/47-channel-user-info from 8263a0ed28 to c37e2a3b31 2026-04-10 16:41:17 -07:00 Compare
Collaborator

We tested the changes functionally and it looked good, still have to look at the code though.

We tested the changes functionally and it looked good, still have to look at the code though.
tepichord left a comment
Collaborator

LGTM past the two things

LGTM past the two things
Dockerfile Outdated
@ -12,11 +12,11 @@ WORKDIR /usr/app/microclimate/server
ARG DATABASE_URL=sqlite:data.db?mode=rwc
RUN sqlx migrate run
RUN cargo build --bin server --release --locked
Collaborator

Do we want this back?

Do we want this back?
seb marked this conversation as resolved
@ -3,2 +9,3 @@
import { IconChartBar } from "@tabler/icons-react";
export default function Home() {
function formatBytes(bytes: number): string {
Collaborator

Could this be in utils?

Could this be in utils?
Owner

+1 on this

+1 on this
seb marked this conversation as resolved
puregarlic requested changes 2026-04-14 17:51:20 -07:00
Dismissed
puregarlic left a comment
Owner

Some feedback. I know a lot of the existing imports are inconsistent, but let's try to use ~/ imports for new code. It can be sort of tough to get Claude to follow, so you'll have to be vigilant.

Some feedback. I know a lot of the existing imports are inconsistent, but let's try to use `~/` imports for new code. It can be sort of tough to get Claude to follow, so you'll have to be vigilant.
Dockerfile Outdated
@ -13,3 +13,3 @@
ARG DATABASE_URL=sqlite:data.db?mode=rwc
RUN sqlx migrate run
RUN cargo build --bin server --release --locked
RUN cargo build --bin server --release
Owner

Better fix this

Better fix this
seb marked this conversation as resolved
Dockerfile Outdated
@ -17,3 +17,3 @@
FROM debian:trixie-slim
RUN apt-get update && apt-get install openssl ca-certificates -y
RUN apt-get update && apt-get install openssl -y
Owner

Fix this too, don’t let ‘em go away

Fix this too, don’t let ‘em go away
seb marked this conversation as resolved
@ -0,0 +40,4 @@
<span
className={cn(
"absolute right-0 bottom-0 rounded-full ring-background",
dotSize === "sm" ? "size-2 ring-1" : "size-3 ring-2",
Owner

I would recommend setting up dotSize like the components from @shadcn/ui, using the cva setup. That should make this cleaner.

I would recommend setting up `dotSize` like the components from `@shadcn/ui`, using the `cva` setup. That should make this cleaner.
seb marked this conversation as resolved
@ -1,5 +1,6 @@
import { invoke } from "@tauri-apps/api/core";
import { useMutation, useQuery } from "@tanstack/react-query";
import { fetchChannels } from "../../lib/utils.ts";
Owner

lib and hooks imports (and generally everything except stuff in the same directory) should be imported using the ~/ prefix instead of ../../.

`lib` and `hooks` imports (and generally everything except stuff in the same directory) should be imported using the `~/` prefix instead of `../../`.
seb marked this conversation as resolved
@ -0,0 +12,4 @@
quality?: string;
}
export function ParticipantSubButton({ did, isSpeaking, quality }: ParticipantSubButtonProps) {
Owner

This component needs to handle the error condition

This component needs to handle the error condition
seb marked this conversation as resolved
@ -0,0 +12,4 @@
queryClient.setQueryData<string[]>(QUERY_KEY, event.payload);
});
const left = listen("channel_left", () => {
Owner

Can we name this event to be channel_exited or something like that. I keep thinking this is left audio channel-specific and it's killing my vibe

Can we name this event to be `channel_exited` or something like that. I keep thinking this is left audio channel-specific and it's killing my vibe
seb marked this conversation as resolved
@ -0,0 +26,4 @@
queryKey: QUERY_KEY,
queryFn: () => [],
initialData: [],
staleTime: Infinity,
Owner

Bold

Bold
seb marked this conversation as resolved
@ -0,0 +28,4 @@
});
return () => {
quality.then((fn) => fn());
Owner

You probably want to wrap these functions in a Promise.all. Since you're not catching them, we could potentially run into a scenario where one throws before the other. You want to await them both and catch their errors.

You probably want to wrap these functions in a `Promise.all`. Since you're not catching them, we could potentially run into a scenario where one throws before the other. You want to await them both and catch their errors.
seb marked this conversation as resolved
@ -0,0 +15,4 @@
useEffect(() => {
const unlistenPromise = listen<RoomStats>("room_stats", (event) => {
setStats(event.payload);
Owner

Is there a reason to wrap this in state instead of the query client?

Is there a reason to wrap this in state instead of the query client?
seb marked this conversation as resolved
seb force-pushed issue/47-channel-user-info from c37e2a3b31 to 764d09a696 2026-04-16 10:17:16 -07:00 Compare
seb force-pushed issue/47-channel-user-info from 764d09a696 to e0b8588efd 2026-04-16 10:23:09 -07:00 Compare
seb requested review from puregarlic 2026-04-16 11:33:05 -07:00
puregarlic approved these changes 2026-04-16 12:11:51 -07:00
seb merged commit adde706771 into main 2026-04-16 13:04:34 -07:00
Sign in to join this conversation.
No description provided.