From 5f11f602fb83e786bd35567d7f35f8016226ba1a Mon Sep 17 00:00:00 2001 From: Mohamed Boudra Date: Fri, 22 May 2026 17:40:42 +0700 Subject: [PATCH] feat(app): add community links and sidebar home button --- .../app/src/components/community-links.tsx | 66 +++++++++++++++++++ .../app/src/components/icons/discord-icon.tsx | 14 ++++ packages/app/src/components/left-sidebar.tsx | 30 ++++++++- .../app/src/screens/open-project-screen.tsx | 17 +++++ packages/app/src/screens/settings-screen.tsx | 7 ++ 5 files changed, 133 insertions(+), 1 deletion(-) create mode 100644 packages/app/src/components/community-links.tsx create mode 100644 packages/app/src/components/icons/discord-icon.tsx diff --git a/packages/app/src/components/community-links.tsx b/packages/app/src/components/community-links.tsx new file mode 100644 index 000000000..bbecedc8d --- /dev/null +++ b/packages/app/src/components/community-links.tsx @@ -0,0 +1,66 @@ +import { useCallback } from "react"; +import { View } from "react-native"; +import { StyleSheet } from "react-native-unistyles"; +import { Heart } from "lucide-react-native"; +import { Button } from "@/components/ui/button"; +import { GitHubIcon } from "@/components/icons/github-icon"; +import { DiscordIcon } from "@/components/icons/discord-icon"; +import { openExternalUrl } from "@/utils/open-external-url"; + +const renderGitHubIcon = (color: string) => ; +const renderDiscordIcon = (color: string) => ; + +export function CommunityLinks() { + const handleOpenGitHub = useCallback(() => { + void openExternalUrl("https://github.com/getpaseo/paseo"); + }, []); + + const handleOpenSponsor = useCallback(() => { + void openExternalUrl("https://github.com/sponsors/boudra"); + }, []); + + const handleOpenDiscord = useCallback(() => { + void openExternalUrl("https://discord.gg/jz8T2uahpH"); + }, []); + + return ( + + + + + + ); +} + +const styles = StyleSheet.create(() => ({ + row: { + flexDirection: "row", + justifyContent: "center", + alignItems: "center", + gap: 0, + }, +})); diff --git a/packages/app/src/components/icons/discord-icon.tsx b/packages/app/src/components/icons/discord-icon.tsx new file mode 100644 index 000000000..d2adf31f3 --- /dev/null +++ b/packages/app/src/components/icons/discord-icon.tsx @@ -0,0 +1,14 @@ +import Svg, { Path } from "react-native-svg"; + +interface DiscordIconProps { + size?: number; + color?: string; +} + +export function DiscordIcon({ size = 16, color = "currentColor" }: DiscordIconProps) { + return ( + + + + ); +} diff --git a/packages/app/src/components/left-sidebar.tsx b/packages/app/src/components/left-sidebar.tsx index b2d8b1774..70b52905b 100644 --- a/packages/app/src/components/left-sidebar.tsx +++ b/packages/app/src/components/left-sidebar.tsx @@ -1,5 +1,5 @@ import { router, usePathname } from "expo-router"; -import { FolderPlus, MessagesSquare, Settings, X } from "lucide-react-native"; +import { FolderPlus, Home, MessagesSquare, Settings, X } from "lucide-react-native"; import { type Dispatch, memo, @@ -58,6 +58,7 @@ import { resolveActiveHost } from "@/utils/active-host"; import { formatConnectionStatus } from "@/utils/daemons"; import { useWindowControlsPadding } from "@/utils/desktop-window"; import { + buildHostOpenProjectRoute, buildHostSessionsRoute, buildSettingsRoute, mapPathnameToServer, @@ -94,6 +95,7 @@ interface SidebarSharedProps { handleRefresh: () => void; handleHostSelect: (nextServerId: string) => void; handleOpenProject: () => void; + handleHome: () => void; handleSettings: () => void; renderHostOption: (input: { option: ComboboxOption; @@ -223,6 +225,17 @@ export const LeftSidebar = memo(function LeftSidebar({ router.push(buildSettingsRoute()); }, []); + const handleHomeMobile = useCallback(() => { + if (!activeServerId) return; + showMobileAgent(); + router.push(buildHostOpenProjectRoute(activeServerId)); + }, [activeServerId, showMobileAgent]); + + const handleHomeDesktop = useCallback(() => { + if (!activeServerId) return; + router.push(buildHostOpenProjectRoute(activeServerId)); + }, [activeServerId]); + const handleViewMoreNavigate = useCallback(() => { if (!activeServerId) { return; @@ -272,6 +285,7 @@ export const LeftSidebar = memo(function LeftSidebar({ isOpen={isOpen} closeToAgent={showMobileAgent} handleOpenProject={handleOpenProjectMobile} + handleHome={handleHomeMobile} handleSettings={handleSettingsMobile} handleViewMoreNavigate={handleViewMoreNavigate} /> @@ -284,6 +298,7 @@ export const LeftSidebar = memo(function LeftSidebar({ insetsTop={insets.top} isOpen={isOpen} handleOpenProject={handleOpenProjectDesktop} + handleHome={handleHomeDesktop} handleSettings={handleSettingsDesktop} handleViewMore={handleViewMoreNavigate} /> @@ -414,6 +429,7 @@ function SidebarFooter({ handleHostSelect, renderHostOption, handleOpenProject, + handleHome, handleSettings, }: { theme: SidebarTheme; @@ -427,6 +443,7 @@ function SidebarFooter({ handleHostSelect: (nextServerId: string) => void; renderHostOption: SidebarSharedProps["renderHostOption"]; handleOpenProject: () => void; + handleHome: () => void; handleSettings: () => void; }) { const newAgentKeys = useShortcutKeys("new-agent"); @@ -456,6 +473,13 @@ function SidebarFooter({ + @@ -758,6 +784,7 @@ function DesktopSidebar({ handleHostSelect, renderHostOption, handleOpenProject, + handleHome, handleSettings, insetsTop, isOpen, @@ -871,6 +898,7 @@ function DesktopSidebar({ handleHostSelect={handleHostSelect} renderHostOption={renderHostOption} handleOpenProject={handleOpenProject} + handleHome={handleHome} handleSettings={handleSettings} /> diff --git a/packages/app/src/screens/open-project-screen.tsx b/packages/app/src/screens/open-project-screen.tsx index 23dec4741..72f33235b 100644 --- a/packages/app/src/screens/open-project-screen.tsx +++ b/packages/app/src/screens/open-project-screen.tsx @@ -4,6 +4,7 @@ import { StyleSheet, useUnistyles } from "react-native-unistyles"; import { useRouter } from "expo-router"; import { FolderOpen, Inbox, Plug, Smartphone } from "lucide-react-native"; import { PaseoLogo } from "@/components/icons/paseo-logo"; +import { CommunityLinks } from "@/components/community-links"; import { MenuHeader } from "@/components/headers/menu-header"; import { useOpenProjectPicker } from "@/hooks/use-open-project-picker"; import { usePanelStore } from "@/stores/panel-store"; @@ -106,6 +107,9 @@ export function OpenProjectScreen({ serverId }: { serverId: string }) { ) : null} + + + ({ fontSize: theme.fontSize.sm, lineHeight: 18, }, + communityRow: { + position: "absolute", + left: 0, + right: 0, + bottom: { + xs: HEADER_INNER_HEIGHT_MOBILE + HEADER_TOP_PADDING_MOBILE + theme.spacing[2], + md: HEADER_INNER_HEIGHT + theme.spacing[2], + }, + flexDirection: "row", + justifyContent: "center", + alignItems: "center", + gap: 0, + }, })); diff --git a/packages/app/src/screens/settings-screen.tsx b/packages/app/src/screens/settings-screen.tsx index 090e7cd98..e7422c7da 100644 --- a/packages/app/src/screens/settings-screen.tsx +++ b/packages/app/src/screens/settings-screen.tsx @@ -56,6 +56,7 @@ import { AddHostModal } from "@/components/add-host-modal"; import { PairLinkModal } from "@/components/pair-link-modal"; import { KeyboardShortcutsSection } from "@/screens/settings/keyboard-shortcuts-section"; import { Button } from "@/components/ui/button"; +import { CommunityLinks } from "@/components/community-links"; import { SegmentedControl } from "@/components/ui/segmented-control"; import { DropdownMenu, @@ -457,6 +458,9 @@ function AboutSection({ appVersionText, isDesktopApp }: AboutSectionProps) { {isDesktopApp ? : null} + + + ); } @@ -1267,6 +1271,9 @@ const styles = StyleSheet.create((theme) => ({ fontSize: theme.fontSize.xs, marginTop: theme.spacing[1], }, + aboutCommunity: { + marginTop: theme.spacing[4], + }, aboutUpdateActions: { flexDirection: "row", alignItems: "center",