feat(app): add community links and sidebar home button

This commit is contained in:
Mohamed Boudra
2026-05-22 17:40:42 +07:00
parent 369c5a4498
commit 5f11f602fb
5 changed files with 133 additions and 1 deletions
@@ -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) => <GitHubIcon color={color} size={14} />;
const renderDiscordIcon = (color: string) => <DiscordIcon color={color} size={14} />;
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 (
<View style={styles.row}>
<Button
variant="ghost"
size="sm"
leftIcon={renderGitHubIcon}
onPress={handleOpenGitHub}
testID="community-links-github-star"
>
Star
</Button>
<Button
variant="ghost"
size="sm"
leftIcon={Heart}
onPress={handleOpenSponsor}
testID="community-links-sponsor"
>
Sponsor
</Button>
<Button
variant="ghost"
size="sm"
leftIcon={renderDiscordIcon}
onPress={handleOpenDiscord}
testID="community-links-discord"
>
Community
</Button>
</View>
);
}
const styles = StyleSheet.create(() => ({
row: {
flexDirection: "row",
justifyContent: "center",
alignItems: "center",
gap: 0,
},
}));
@@ -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 (
<Svg width={size} height={size} viewBox="0 0 24 24" fill={color}>
<Path d="M20.317 4.3698a19.7913 19.7913 0 00-4.8851-1.5152.0741.0741 0 00-.0785.0371c-.211.3753-.4447.8648-.6083 1.2495-1.8447-.2762-3.68-.2762-5.4868 0-.1636-.3933-.4058-.8742-.6177-1.2495a.077.077 0 00-.0785-.037 19.7363 19.7363 0 00-4.8852 1.515.0699.0699 0 00-.0321.0277C.5334 9.0458-.319 13.5799.0992 18.0578a.0824.0824 0 00.0312.0561c2.0528 1.5076 4.0413 2.4228 5.9929 3.0294a.0777.0777 0 00.0842-.0276c.4616-.6304.8731-1.2952 1.226-1.9942a.076.076 0 00-.0416-.1057c-.6528-.2476-1.2743-.5495-1.8722-.8923a.077.077 0 01-.0076-.1277c.1258-.0943.2517-.1923.3718-.2914a.0743.0743 0 01.0776-.0105c3.9278 1.7933 8.18 1.7933 12.0614 0a.0739.0739 0 01.0785.0095c.1202.099.246.1981.3728.2924a.077.077 0 01-.0066.1276 12.2986 12.2986 0 01-1.873.8914.0766.0766 0 00-.0407.1067c.3604.698.7719 1.3628 1.225 1.9932a.076.076 0 00.0842.0286c1.961-.6067 3.9495-1.5219 6.0023-3.0294a.077.077 0 00.0313-.0552c.5004-5.177-.8382-9.6739-3.5485-13.6604a.061.061 0 00-.0312-.0286zM8.02 15.3312c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9555-2.4189 2.157-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.9555 2.4189-2.1569 2.4189zm7.9748 0c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9554-2.4189 2.1569-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.946 2.4189-2.1568 2.4189Z" />
</Svg>
);
}
+29 -1
View File
@@ -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({
<AddProjectTooltipContent newAgentKeys={newAgentKeys} />
</TooltipContent>
</Tooltip>
<FooterIconButton
onPress={handleHome}
testID="sidebar-home"
accessibilityLabel="Home"
icon={Home}
theme={theme}
/>
<FooterIconButton
onPress={handleSettings}
testID="sidebar-settings"
@@ -501,6 +525,7 @@ function MobileSidebar({
handleHostSelect,
renderHostOption,
handleOpenProject,
handleHome,
handleSettings,
insetsTop,
insetsBottom,
@@ -729,6 +754,7 @@ function MobileSidebar({
handleHostSelect={handleHostSelect}
renderHostOption={renderHostOption}
handleOpenProject={handleOpenProject}
handleHome={handleHome}
handleSettings={handleSettings}
/>
</View>
@@ -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}
/>
@@ -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}
</View>
</View>
<View style={styles.communityRow}>
<CommunityLinks />
</View>
<PairDeviceModal
visible={isPairDeviceOpen}
onClose={handleClosePairDevice}
@@ -227,4 +231,17 @@ const styles = StyleSheet.create((theme) => ({
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,
},
}));
@@ -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) {
</View>
{isDesktopApp ? <DesktopAppUpdateRow /> : null}
</View>
<View style={styles.aboutCommunity}>
<CommunityLinks />
</View>
</SettingsSection>
);
}
@@ -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",