Skip to content
Merged
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
64 changes: 50 additions & 14 deletions desktop/src/app/AppShell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ import {
import { formatTimelineMessages } from "@/features/messages/lib/formatTimelineMessages";
import { MessageComposer } from "@/features/messages/ui/MessageComposer";
import { MessageTimeline } from "@/features/messages/ui/MessageTimeline";
import { ProfileSheet } from "@/features/profile/ui/ProfileSheet";
import { SearchDialog } from "@/features/search/ui/SearchDialog";
import { SettingsView } from "@/features/settings/ui/SettingsView";
import { AppSidebar } from "@/features/sidebar/ui/AppSidebar";
import { getEventById } from "@/shared/api/tauri";
import { useIdentityQuery } from "@/shared/api/hooks";
Expand All @@ -33,7 +33,7 @@ import {
SidebarTrigger,
} from "@/shared/ui/sidebar";

type AppView = "home" | "channel";
type AppView = "home" | "channel" | "settings";

function createSearchAnchorEvent(hit: SearchHit): RelayEvent {
return {
Expand All @@ -51,7 +51,6 @@ export function AppShell() {
const [selectedView, setSelectedView] = React.useState<AppView>("home");
const [isChannelManagementOpen, setIsChannelManagementOpen] =
React.useState(false);
const [isProfileOpen, setIsProfileOpen] = React.useState(false);
const [isSearchOpen, setIsSearchOpen] = React.useState(false);
const [searchAnchor, setSearchAnchor] = React.useState<SearchHit | null>(
null,
Expand Down Expand Up @@ -129,7 +128,11 @@ export function AppShell() {
.join(" ") || "Channel details and activity."
: "Connect to the relay to browse channels and read messages.";
const contentPaneKey =
selectedView === "home" ? "home" : `channel:${activeChannel?.id ?? "none"}`;
selectedView === "home"
? "home"
: selectedView === "settings"
? "settings"
: `channel:${activeChannel?.id ?? "none"}`;
const isTimelineLoading =
messagesQuery.isLoading && resolvedMessages.length === 0;

Expand All @@ -143,6 +146,15 @@ export function AppShell() {
[setSelectedChannelId],
);

const handleOpenSettings = React.useCallback(() => {
setIsSearchOpen(false);
setIsChannelManagementOpen(false);

React.startTransition(() => {
setSelectedView("settings");
});
}, []);

const handleOpenSearchResult = React.useCallback(
(hit: SearchHit) => {
setSearchAnchor(hit);
Expand Down Expand Up @@ -171,6 +183,28 @@ export function AppShell() {
[handleOpenChannel],
);

React.useEffect(() => {
function handleKeyDown(event: KeyboardEvent) {
const isSettingsShortcut =
(event.key === "," || event.code === "Comma") &&
(event.metaKey || event.ctrlKey) &&
!event.altKey &&
!event.shiftKey;

if (!isSettingsShortcut) {
return;
}

event.preventDefault();
handleOpenSettings();
}

window.addEventListener("keydown", handleKeyDown);
return () => {
window.removeEventListener("keydown", handleKeyDown);
};
}, [handleOpenSettings]);

return (
<SidebarProvider className="h-dvh overflow-hidden overscroll-none">
<SidebarTrigger className="fixed left-[80px] top-[9px] z-50 h-6 w-6" />
Expand Down Expand Up @@ -200,9 +234,6 @@ export function AppShell() {
onOpenSearch={() => {
setIsSearchOpen(true);
}}
onOpenProfile={() => {
setIsProfileOpen(true);
}}
onSelectHome={() => {
React.startTransition(() => {
setSelectedView("home");
Expand All @@ -211,6 +242,7 @@ export function AppShell() {
void homeFeedQuery.refetch();
}}
onSelectChannel={handleOpenChannel}
onSelectSettings={handleOpenSettings}
selectedChannelId={selectedChannel?.id ?? null}
selectedView={selectedView}
/>
Expand All @@ -236,6 +268,12 @@ export function AppShell() {
mode="home"
title="Home"
/>
) : selectedView === "settings" ? (
<ChatHeader
description="Theme, appearance, and profile preferences for your current identity."
mode="settings"
title="Settings"
/>
) : (
<ChatHeader
actions={
Expand Down Expand Up @@ -278,6 +316,11 @@ export function AppShell() {
void homeFeedQuery.refetch();
}}
/>
) : selectedView === "settings" ? (
<SettingsView
currentPubkey={identityQuery.data?.pubkey}
fallbackDisplayName={identityQuery.data?.displayName}
/>
) : (
<>
<MessageTimeline
Expand Down Expand Up @@ -353,13 +396,6 @@ export function AppShell() {
onOpenChange={setIsChannelManagementOpen}
open={isChannelManagementOpen && activeChannel !== null}
/>

<ProfileSheet
currentPubkey={identityQuery.data?.pubkey}
fallbackDisplayName={identityQuery.data?.displayName}
onOpenChange={setIsProfileOpen}
open={isProfileOpen}
/>
</SidebarInset>
</SidebarProvider>
);
Expand Down
10 changes: 7 additions & 3 deletions desktop/src/features/chat/ui/ChatHeader.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { getCurrentWindow } from "@tauri-apps/api/window";
import { CircleDot, FileText, Hash, Home } from "lucide-react";
import { CircleDot, FileText, Hash, Home, Settings2 } from "lucide-react";
import type * as React from "react";

import type { ChannelType } from "@/shared/api/types";
Expand All @@ -9,20 +9,24 @@ type ChatHeaderProps = {
title: string;
description: string;
channelType?: ChannelType;
mode?: "home" | "channel";
mode?: "home" | "channel" | "settings";
};

function ChannelIcon({
channelType,
mode = "channel",
}: {
channelType?: ChannelType;
mode?: "home" | "channel";
mode?: "home" | "channel" | "settings";
}) {
if (mode === "home") {
return <Home className="h-5 w-5 text-primary" />;
}

if (mode === "settings") {
return <Settings2 className="h-5 w-5 text-primary" />;
}

if (channelType === "dm") {
return <CircleDot className="h-5 w-5 text-primary" />;
}
Expand Down
Loading
Loading