Skip to content
Merged

Dev #38

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions apps/web/src/components/allies/allies-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@ import { Button } from "@repo/ui/components/button"
import { Input } from "@repo/ui/components/input"
import { ScrollArea } from "@repo/ui/components/scroll-area"
import { Skeleton } from "@repo/ui/components/skeleton"
import { useIsMobile } from "@repo/ui/hooks/use-mobile"
import { cn } from "@repo/ui/lib/utils"
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"
import {
Check,
Menu,
MessageCircle,
Search,
ShieldOff,
Expand All @@ -28,6 +30,7 @@ import {
import { useState } from "react"
import { toast } from "sonner"
import { UserAvatar } from "@/components/ui/user-avatar"
import { useMobileSidebar } from "@/context/mobile-sidebar-context"
import { useBlockedUsers } from "@/hooks/use-blocked-users"
import { useCreateDM } from "@/hooks/use-create-dm"
import { apiClient } from "@/lib/api-client"
Expand Down Expand Up @@ -205,6 +208,8 @@ function BlockedUserRow({
export function AlliesPage() {
const queryClient = useQueryClient()
const createDM = useCreateDM()
const isMobile = useIsMobile()
const { setOpen: openMobileSidebar } = useMobileSidebar()
const [tab, setTab] = useState<Tab>("all")
const [search, setSearch] = useState("")
const [addUsername, setAddUsername] = useState("")
Expand Down Expand Up @@ -391,6 +396,15 @@ export function AlliesPage() {
<div className="flex h-full flex-1 flex-col bg-background">
{/* Header */}
<div className="flex h-12 shrink-0 items-center gap-3 border-b border-border px-4">
{isMobile && (
<button
type="button"
onClick={() => openMobileSidebar(true)}
className="rounded-sm p-1.5 text-muted-foreground hover:bg-accent hover:text-foreground"
>
<Menu className="size-5" />
</button>
)}
<Users className="size-5 text-muted-foreground" />
<h1 className="text-base font-semibold">Allies</h1>
<div className="mx-2 h-5 w-px bg-border" />
Expand Down
74 changes: 58 additions & 16 deletions apps/web/src/components/chat/header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@ import {
TooltipContent,
TooltipTrigger,
} from "@repo/ui/components/tooltip"
import { Hash, PanelRight, Pin } from "lucide-react"
import { useIsMobile } from "@repo/ui/hooks/use-mobile"
import { useParams } from "@tanstack/react-router"
import { Hash, Menu, PanelRight, Pin } from "lucide-react"
import { useRightSidebar } from "@/components/sidebar/right-panel/right-sidebar-context"
import { useMobileSidebar } from "@/context/mobile-sidebar-context"
import { HeaderSearch } from "./header-search"

export type ChatContext =
Expand All @@ -27,10 +30,23 @@ export function ChatHeader({
channelId: string
onTogglePinnedMessages?: () => void
}) {
const { isCollapsed, toggleCollapsed } = useRightSidebar()
const { view, setView, clearView, isCollapsed, toggleCollapsed } =
useRightSidebar()
const isMobile = useIsMobile()
const { setOpen: openMobileSidebar } = useMobileSidebar()
const { guildSlug } = useParams({ strict: false })

return (
<div className="flex h-12 shrink-0 items-center gap-2 border-b border-border px-4">
{isMobile && (
<button
type="button"
onClick={() => openMobileSidebar(true)}
className="rounded-sm p-1.5 text-muted-foreground hover:bg-accent hover:text-foreground"
>
<Menu className="size-5" />
</button>
Comment on lines +41 to +48
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Give the new icon-only mobile controls accessible names.

The new Menu and Members buttons render only icons. Tooltips do not provide a reliable accessible name, so screen readers get unlabeled controls for two primary mobile actions. Add aria-labels; if the Members button remains a toggle, expose its state with aria-pressed too.

Suggested fix
         <button
           type="button"
           onClick={() => openMobileSidebar(true)}
+          aria-label="Open navigation menu"
           className="rounded-sm p-1.5 text-muted-foreground hover:bg-accent hover:text-foreground"
         >
@@
                 <button
                   type="button"
                   onClick={() => {
                     if (view) {
                       clearView()
                     } else {
                       setView({
                         type: "guild-members",
                         guildSlug: guildSlug ?? "",
                         channelId,
                       })
                     }
                   }}
+                  aria-label="Toggle members panel"
                   className="rounded-sm p-1.5 text-muted-foreground hover:bg-accent hover:text-foreground"
                 >

Also applies to: 98-119

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/components/chat/header.tsx` around lines 41 - 48, The Menu and
Members icon-only buttons rendered in the Chat header (conditional on isMobile
and calling openMobileSidebar/openMobileMembers or similar handlers) lack
accessible names; add aria-label attributes (e.g., aria-label="Open menu" for
the Menu button and aria-label="Members" for the Members button) to both buttons
and, if the Members button is a toggle, also expose its toggle state with
aria-pressed={isMembersOpen} (or the appropriate state variable) so screen
readers receive a meaningful name and state.

)}
{context.type === "channel" && (
<Hash className="size-5 shrink-0 text-muted-foreground" />
)}
Expand Down Expand Up @@ -77,20 +93,46 @@ export function ChatHeader({
<TooltipContent>Pinned Messages</TooltipContent>
</Tooltip>
)}
{isCollapsed && context.type === "channel" && (
<Tooltip>
<TooltipTrigger asChild>
<button
type="button"
onClick={toggleCollapsed}
className="rounded-sm p-1.5 text-muted-foreground hover:bg-accent hover:text-foreground"
>
<PanelRight className="size-4" />
</button>
</TooltipTrigger>
<TooltipContent>Show Panel</TooltipContent>
</Tooltip>
)}
{context.type === "channel" &&
(isMobile ? (
<Tooltip>
<TooltipTrigger asChild>
<button
type="button"
onClick={() => {
if (view) {
clearView()
} else {
setView({
type: "guild-members",
guildSlug: guildSlug ?? "",
channelId,
})
}
}}
className="rounded-sm p-1.5 text-muted-foreground hover:bg-accent hover:text-foreground"
>
<PanelRight className="size-4" />
</button>
</TooltipTrigger>
<TooltipContent>Members</TooltipContent>
</Tooltip>
) : (
isCollapsed && (
<Tooltip>
<TooltipTrigger asChild>
<button
type="button"
onClick={toggleCollapsed}
className="rounded-sm p-1.5 text-muted-foreground hover:bg-accent hover:text-foreground"
>
<PanelRight className="size-4" />
</button>
</TooltipTrigger>
<TooltipContent>Show Panel</TooltipContent>
</Tooltip>
)
))}
</div>
</div>
)
Expand Down
12 changes: 6 additions & 6 deletions apps/web/src/components/onboarding/onboarding-dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -152,11 +152,11 @@ export function OnboardingDialog({ open }: { open: boolean }) {
if (!channelsRes.ok) return null

const channels = await channelsRes.json()
return (
channels.uncategorized[0]?.id ??
channels.categories[0]?.channels[0]?.id ??
null
)
if (channels.uncategorized[0]?.id) return channels.uncategorized[0].id
for (const cat of channels.categories) {
if (cat.channels[0]?.id) return cat.channels[0].id
}
return null
}

const handleCreate = async (e: React.FormEvent) => {
Expand Down Expand Up @@ -477,7 +477,7 @@ export function OnboardingDialog({ open }: { open: boolean }) {
<Label htmlFor="invite-link">Invite Link or Code</Label>
<Input
id="invite-link"
placeholder="https://townhall.chat/invite/abc123 or abc123"
placeholder="https://app.townhall.chat/invite/abc123 or abc123"
value={inviteLink}
onChange={(e) => setInviteLink(e.target.value)}
disabled={loading}
Expand Down
12 changes: 8 additions & 4 deletions apps/web/src/components/sidebar/channel-panel/channel-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import {
} from "lucide-react"
import { AnimatePresence, motion } from "motion/react"
import { useCallback, useState } from "react"
import { useMobileSidebar } from "@/context/mobile-sidebar-context"
import { useUnread } from "@/context/unread-context"
import { apiClient } from "@/lib/api-client"
import type { Channel, ListChannelsResponse } from "@/lib/api-types"
Expand Down Expand Up @@ -109,6 +110,7 @@ export function ChannelList() {
const { guildSlug, channelId: activeChannelId } = useParams({ strict: false })
const navigate = useNavigate()
const queryClient = useQueryClient()
const { setOpen: closeMobileSidebar } = useMobileSidebar()

const { data, isPending } = useQuery({
queryKey: ["channels", guildSlug],
Expand Down Expand Up @@ -386,15 +388,16 @@ export function ChannelList() {
active={activeChannelId === ch.id}
canManage={canManage}
canDelete={canDelete}
onClick={() =>
onClick={() => {
navigate({
to: "/$guildSlug/$channelId",
params: {
guildSlug: guildSlug as string,
channelId: ch.id,
},
})
}
closeMobileSidebar(false)
}}
/>
))}
</div>
Expand All @@ -417,12 +420,13 @@ export function ChannelList() {
activeChannelId={activeChannelId}
canManage={canManage}
canDelete={canDelete}
onChannelClick={(channelId) =>
onChannelClick={(channelId) => {
navigate({
to: "/$guildSlug/$channelId",
params: { guildSlug: guildSlug as string, channelId },
})
}
closeMobileSidebar(false)
}}
/>
))}
</SortableContext>
Expand Down
7 changes: 5 additions & 2 deletions apps/web/src/components/sidebar/dm-panel/dm-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
import { cn } from "@repo/ui/lib/utils"
import { useQuery } from "@tanstack/react-query"
import { useNavigate, useParams } from "@tanstack/react-router"
import { useMobileSidebar } from "@/context/mobile-sidebar-context"
import { useUnread } from "@/context/unread-context"
import { apiClient } from "@/lib/api-client"
import type { DMember } from "@/lib/api-types"
Expand All @@ -16,6 +17,7 @@ import { UserAvatar } from "../../ui/user-avatar"
export function DMList() {
const navigate = useNavigate()
const { dmId } = useParams({ strict: false })
const { setOpen: closeMobileSidebar } = useMobileSidebar()

const { data } = useQuery({
queryKey: ["dms"],
Expand Down Expand Up @@ -52,9 +54,10 @@ export function DMList() {
lastMessageAuthor={dm.lastMessage?.author.name ?? null}
isGroupDM={dm.type === "group_dm"}
active={dmId === dm.id}
onClick={() =>
onClick={() => {
navigate({ to: "/dms/$dmId", params: { dmId: dm.id } })
}
closeMobileSidebar(false)
}}
/>
)
})}
Expand Down
7 changes: 6 additions & 1 deletion apps/web/src/components/sidebar/dm-panel/dm-panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { cn } from "@repo/ui/lib/utils"
import { useNavigate, useParams } from "@tanstack/react-router"
import { Plus, Users } from "lucide-react"
import { useState } from "react"
import { useMobileSidebar } from "@/context/mobile-sidebar-context"
import { SearchBar } from "../channel-panel/search-bar"
import { UserBar } from "../channel-panel/user-bar"
import { DMList } from "./dm-list"
Expand All @@ -12,6 +13,7 @@ import { NewDMDialog } from "./new-dm-dialog"
export function DMPanel() {
const navigate = useNavigate()
const { dmId } = useParams({ strict: false })
const { setOpen: closeMobileSidebar } = useMobileSidebar()
const [newDMOpen, setNewDMOpen] = useState(false)

return (
Expand All @@ -20,7 +22,10 @@ export function DMPanel() {
<div className="space-y-0.5 px-2 pt-3">
<button
type="button"
onClick={() => navigate({ to: "/dms" })}
onClick={() => {
navigate({ to: "/dms" })
closeMobileSidebar(false)
}}
className={cn(
"flex w-full items-center gap-2.5 rounded-lg px-2 py-1.5 text-[14px] font-medium hover:bg-foreground/[0.06]",
!dmId
Expand Down
Loading
Loading