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
20 changes: 17 additions & 3 deletions apps/dashboard/src/components/layouts/dashboard-mobile-nav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
ReviewsIcon,
SunIcon,
SystemIcon,
UserCircleIcon,
} from "@diffkit/icons";
import { Avatar, AvatarFallback } from "@diffkit/ui/components/avatar";
import {
Expand All @@ -20,10 +21,13 @@ import {
DropdownMenuTrigger,
} from "@diffkit/ui/components/dropdown-menu";
import { cn } from "@diffkit/ui/lib/utils";
import { useQuery } from "@tanstack/react-query";
import { Link } from "@tanstack/react-router";
import { useTheme } from "next-themes";
import { useState } from "react";
import { signOutToLogin } from "#/lib/auth-actions";
import { githubViewerQueryOptions } from "#/lib/github.query";
import { useHasMounted } from "#/lib/use-has-mounted";

const themeOptions = [
{ value: "light", icon: SunIcon, label: "Light" },
Expand All @@ -40,6 +44,7 @@ interface MobileNavItem {

interface DashboardMobileNavProps {
user: {
id: string;
name?: string | null;
email: string;
image?: string | null;
Expand All @@ -59,6 +64,12 @@ export function DashboardMobileNav({
}: DashboardMobileNavProps) {
const { theme, setTheme } = useTheme();
const [avatarLoadFailed, setAvatarLoadFailed] = useState(false);
const hasMounted = useHasMounted();
const viewerQuery = useQuery({
...githubViewerQueryOptions({ userId: user.id }),
enabled: hasMounted,
});
const viewerLogin = viewerQuery.data?.login;

const displayName = user.name ?? user.email;
const initials = displayName
Expand Down Expand Up @@ -161,9 +172,12 @@ export function DashboardMobileNav({
</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuGroup>
<DropdownMenuItem>
Profile
<DropdownMenuShortcut keys={["G", "P"]} />
<DropdownMenuItem asChild disabled={!viewerLogin}>
<Link to="/$owner" params={{ owner: viewerLogin ?? "" }}>
<UserCircleIcon size={16} strokeWidth={2} />
Profile
<DropdownMenuShortcut keys={["G", "U"]} />
</Link>
</DropdownMenuItem>
<DropdownMenuItem asChild>
<Link to="/settings">
Expand Down
41 changes: 22 additions & 19 deletions apps/dashboard/src/components/layouts/dashboard-tabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,10 @@ interface DashboardTabsProps {
export function DashboardTabs({ tabsReady, routerRef }: DashboardTabsProps) {
const openTabs = useTabs();
const pathname = useRouterState({ select: (s) => s.location.pathname });
const contextTabRef = useRef<{ tab: Tab; index: number } | null>(null);
const [contextTab, setContextTab] = useState<{
tab: Tab;
index: number;
} | null>(null);
const {
scrollRef,
canScrollLeft,
Expand Down Expand Up @@ -119,34 +122,34 @@ export function DashboardTabs({ tabsReady, routerRef }: DashboardTabsProps) {
const handleCloseTab = useCallback(
(id: string, tabUrl: string) => {
const isActive = pathname === tabUrl;
const index = openTabs.findIndex((tab) => tab.id === id);
const nextTab =
index === -1 ? undefined : (openTabs[index + 1] ?? openTabs[index - 1]);
removeTab(id);
if (isActive) {
void routerRef.current.navigate({ to: "/" });
void routerRef.current.navigate({ to: nextTab?.url ?? "/" });
}
},
[pathname, routerRef],
[openTabs, pathname, routerRef],
);

const handleContextClose = useCallback(() => {
const ctx = contextTabRef.current;
if (!ctx) return;
handleCloseTab(ctx.tab.id, ctx.tab.url);
}, [handleCloseTab]);
if (!contextTab) return;
handleCloseTab(contextTab.tab.id, contextTab.tab.url);
}, [contextTab, handleCloseTab]);

const handleContextCloseOthers = useCallback(() => {
const ctx = contextTabRef.current;
if (!ctx) return;
if (pathname !== ctx.tab.url) {
void routerRef.current.navigate({ to: ctx.tab.url });
if (!contextTab) return;
if (pathname !== contextTab.tab.url) {
void routerRef.current.navigate({ to: contextTab.tab.url });
}
removeOtherTabs(ctx.tab.id);
}, [pathname, routerRef]);
removeOtherTabs(contextTab.tab.id);
}, [contextTab, pathname, routerRef]);

const handleContextCloseRight = useCallback(() => {
const ctx = contextTabRef.current;
if (!ctx) return;
removeTabsToRight(ctx.tab.id);
}, []);
if (!contextTab) return;
removeTabsToRight(contextTab.tab.id);
}, [contextTab]);

if (openTabs.length === 0) return null;

Expand Down Expand Up @@ -193,7 +196,7 @@ export function DashboardTabs({ tabsReady, routerRef }: DashboardTabsProps) {
icon={Icon}
onClose={handleCloseTab}
onContextMenu={() => {
contextTabRef.current = { tab, index };
setContextTab({ tab, index });
}}
routerRef={routerRef}
/>
Expand All @@ -216,7 +219,7 @@ export function DashboardTabs({ tabsReady, routerRef }: DashboardTabsProps) {
</ContextMenuItem>
<ContextMenuItem
onSelect={handleContextCloseRight}
disabled={contextTabRef.current?.index === openTabs.length - 1}
disabled={!contextTab || contextTab.index === openTabs.length - 1}
>
<ChevronRightIcon size={14} strokeWidth={2} />
Close tabs to the right
Expand Down
33 changes: 31 additions & 2 deletions apps/dashboard/src/components/layouts/dashboard-topbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ import {
githubViewerQueryOptions,
} from "#/lib/github.query";
import { useGlobalShortcuts } from "#/lib/shortcuts";
import { type Tab, useTabs } from "#/lib/tab-store";
import { removeTab, type Tab, useTabs } from "#/lib/tab-store";
import { useHasMounted } from "#/lib/use-has-mounted";

interface DashboardTopbarProps {
Expand Down Expand Up @@ -154,6 +154,13 @@ export function DashboardTopbar({
void routerRef.current.navigate({ to: tab.url });
}

function getNeighborTab(tab: Tab | undefined) {
if (!tab) return undefined;
const index = openTabs.findIndex((item) => item.id === tab.id);
if (index === -1) return undefined;
return openTabs[index + 1] ?? openTabs[index - 1];
}

useGlobalShortcuts([
...Array.from(
{ length: Math.min(openTabs.length, MAX_TAB_SHORTCUTS) },
Expand All @@ -165,6 +172,28 @@ export function DashboardTopbar({
},
}),
),
{
shortcut: [{ key: "g" }, { key: "u" }],
enabled: tabsReady && Boolean(viewerLogin),
onTrigger: () => {
void routerRef.current.navigate({
to: "/$owner",
params: { owner: viewerLogin ?? "" },
});
},
},
{
shortcut: { key: "w", shift: true },
enabled: tabsReady && openTabs.length > 0,
onTrigger: () => {
const currentPath = routerRef.current.state.location.pathname;
const currentTab = openTabs.find((tab) => tab.url === currentPath);
if (!currentTab) return;
const nextTab = getNeighborTab(currentTab);
removeTab(currentTab.id);
void routerRef.current.navigate({ to: nextTab?.url ?? "/" });
},
},
{
shortcut: { key: "ArrowLeft", shift: true },
enabled: tabsReady && openTabs.length > 1,
Expand Down Expand Up @@ -252,7 +281,7 @@ export function DashboardTopbar({
<Link to="/$owner" params={{ owner: viewerLogin ?? "" }}>
<UserCircleIcon size={16} strokeWidth={2} />
Profile
<DropdownMenuShortcut keys={["G", "P"]} />
<DropdownMenuShortcut keys={["G", "U"]} />
</Link>
</DropdownMenuItem>
<DropdownMenuItem asChild>
Expand Down
19 changes: 19 additions & 0 deletions apps/dashboard/src/lib/command-palette/use-command-items.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
GitPullRequestDraftIcon,
GitPullRequestIcon,
IssuesIcon,
UserCircleIcon,
} from "@diffkit/icons";
import { type QueryClient, useQueryClient } from "@tanstack/react-query";
import { getRouteApi } from "@tanstack/react-router";
Expand Down Expand Up @@ -70,9 +71,27 @@ export function useCommandItems(): CommandItem[] {
const issues = queryClient.getQueryData<MyIssuesResult>(
githubQueryKeys.issues.mine(scope),
);
const viewer = queryClient.getQueryData<{ login: string } | null>(
githubQueryKeys.viewer(scope),
);

const dynamicItems: CommandItem[] = [];

if (viewer) {
dynamicItems.push({
id: "nav:profile",
label: "Go to Profile",
group: "Pages",
icon: UserCircleIcon,
keywords: ["user", "me", viewer.login],
shortcut: ["G", "U"],
action: {
type: "navigate",
to: `/${viewer.login}`,
},
});
}

if (repos) {
for (const repo of repos) {
dynamicItems.push({
Expand Down
2 changes: 2 additions & 0 deletions apps/dashboard/src/routes/_protected/settings/shortcuts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ const shortcutGroups: ShortcutGroup[] = [
title: "Navigation",
shortcuts: [
{ keys: ["G", "H"], description: "Go to Overview" },
{ keys: ["G", "U"], description: "Go to Profile" },
{ keys: ["G", "P"], description: "Go to Pull Requests" },
{ keys: ["G", "I"], description: "Go to Issues" },
{ keys: ["G", "R"], description: "Go to Reviews" },
Expand All @@ -210,6 +211,7 @@ const shortcutGroups: ShortcutGroup[] = [
title: "Tabs",
shortcuts: [
{ keys: ["Shift", "1\u20139"], description: "Switch to tab by position" },
{ keys: ["Shift", "W"], description: "Close current tab" },
{ keys: ["Shift", "\u2190"], description: "Previous tab" },
{ keys: ["Shift", "\u2192"], description: "Next tab" },
],
Expand Down
Loading