diff --git a/web/components/contract-search.tsx b/web/components/contract-search.tsx index 6044178e..a0126b2e 100644 --- a/web/components/contract-search.tsx +++ b/web/components/contract-search.tsx @@ -389,9 +389,7 @@ function ContractSearchControls(props: { } return ( - + )} -
+
diff --git a/web/components/contract/contracts-grid.tsx b/web/components/contract/contracts-grid.tsx index fcf20f02..3da9a5d5 100644 --- a/web/components/contract/contracts-grid.tsx +++ b/web/components/contract/contracts-grid.tsx @@ -110,6 +110,7 @@ export function CreatorContractsList(props: { return ( void +}) { + const { currentPage } = props + const user = useUser() + + return ( + + ) +} + +function NavBarItem(props: { + item: Item + currentPage: string + onClick: (key: string) => void +}) { + const { item, currentPage } = props + const track = trackCallback( + `group navbar: ${item.trackingEventName ?? item.name}` + ) + + return ( + + ) +} diff --git a/web/components/nav/group-sidebar.tsx b/web/components/nav/group-sidebar.tsx new file mode 100644 index 00000000..3735adc7 --- /dev/null +++ b/web/components/nav/group-sidebar.tsx @@ -0,0 +1,90 @@ +import { ClipboardIcon, HomeIcon } from '@heroicons/react/outline' +import clsx from 'clsx' +import { useUser } from 'web/hooks/use-user' +import { ManifoldLogo } from './manifold-logo' +import { ProfileSummary } from './profile-menu' +import React from 'react' +import TrophyIcon from 'web/lib/icons/trophy-icon' +import { SignInButton } from '../sign-in-button' +import CornerDownRightIcon from 'web/lib/icons/corner-down-right-icon' +import NotificationsIcon from '../notifications-icon' +import { SidebarItem } from './sidebar' +import { buildArray } from 'common/util/array' +import { User } from 'common/user' +import { Row } from '../layout/row' +import { Col } from '../layout/col' + +const groupNavigation = [ + { name: 'Markets', key: 'markets', icon: HomeIcon }, + { name: 'About', key: 'about', icon: ClipboardIcon }, + { name: 'Leaderboard', key: 'leaderboards', icon: TrophyIcon }, +] + +const generalNavigation = (user?: User | null) => + buildArray( + user && { + name: 'Notifications', + href: `/notifications`, + key: 'notifications', + icon: NotificationsIcon, + } + ) + +export function GroupSidebar(props: { + groupName: string + className?: string + onClick: (key: string) => void + joinOrAddQuestionsButton: React.ReactNode + currentKey: string +}) { + const { className, groupName, currentKey } = props + + const user = useUser() + + return ( + + ) +} diff --git a/web/components/nav/nav-bar.tsx b/web/components/nav/nav-bar.tsx index a07fa0ad..778cdd1a 100644 --- a/web/components/nav/nav-bar.tsx +++ b/web/components/nav/nav-bar.tsx @@ -17,6 +17,8 @@ import { useRouter } from 'next/router' import NotificationsIcon from 'web/components/notifications-icon' import { useIsIframe } from 'web/hooks/use-is-iframe' import { trackCallback } from 'web/lib/service/analytics' +import { User } from 'common/user' + import { PAST_BETS } from 'common/user' function getNavigation() { @@ -35,6 +37,21 @@ const signedOutNavigation = [ { name: 'Explore', href: '/home', icon: SearchIcon }, ] +export const userProfileItem = (user: User) => ({ + name: formatMoney(user.balance), + trackingEventName: 'profile', + href: `/${user.username}?tab=${PAST_BETS}`, + icon: () => ( + + ), +}) + // From https://codepen.io/chris__sev/pen/QWGvYbL export function BottomNavBar() { const [sidebarOpen, setSidebarOpen] = useState(false) @@ -62,20 +79,7 @@ export function BottomNavBar() { ( - - ), - }} + item={userProfileItem(user)} /> )}
+ ( + return buildArray( CHALLENGES_ENABLED && { name: 'Challenges', href: '/challenges' }, [ { name: 'Groups', href: '/groups' }, @@ -156,39 +156,59 @@ function getMoreMobileNav() { export type Item = { name: string trackingEventName?: string - href: string + href?: string + key?: string icon?: React.ComponentType<{ className?: string }> } -function SidebarItem(props: { item: Item; currentPage: string }) { - const { item, currentPage } = props - return ( - - - {item.icon && ( - - +export function SidebarItem(props: { + item: Item + currentPage: string + onClick?: (key: string) => void +}) { + const { item, currentPage, onClick } = props + const isCurrentPage = + item.href != null ? item.href === currentPage : item.key === currentPage + + const sidebarItem = ( + + {item.icon && ( + ) + + if (item.href) { + return ( + + {sidebarItem} + + ) + } else { + return onClick ? ( + + ) : ( + <> + ) + } } function SidebarButton(props: { diff --git a/web/lib/icons/corner-down-right-icon.tsx b/web/lib/icons/corner-down-right-icon.tsx new file mode 100644 index 00000000..37d61afa --- /dev/null +++ b/web/lib/icons/corner-down-right-icon.tsx @@ -0,0 +1,19 @@ +export default function CornerDownRightIcon(props: { className?: string }) { + return ( + + + + + ) +} diff --git a/web/pages/experimental/home/index.tsx b/web/pages/experimental/home/index.tsx index f5734918..2d3270aa 100644 --- a/web/pages/experimental/home/index.tsx +++ b/web/pages/experimental/home/index.tsx @@ -114,6 +114,7 @@ function SearchSection(props: { } noControls maxResults={6} + headerClassName="sticky" persistPrefix={`experimental-home-${sort}`} /> @@ -135,6 +136,7 @@ function GroupSection(props: { additionalFilter={{ groupSlug: group.slug }} noControls maxResults={6} + headerClassName="sticky" persistPrefix={`experimental-home-${group.slug}`} /> diff --git a/web/pages/group/[...slugs]/index.tsx b/web/pages/group/[...slugs]/index.tsx index 70b06ac5..1edcc638 100644 --- a/web/pages/group/[...slugs]/index.tsx +++ b/web/pages/group/[...slugs]/index.tsx @@ -1,10 +1,9 @@ import React, { useState } from 'react' import Link from 'next/link' import { useRouter } from 'next/router' -import { toast } from 'react-hot-toast' +import { toast, Toaster } from 'react-hot-toast' import { Group, GROUP_CHAT_SLUG } from 'common/group' -import { Page } from 'web/components/page' import { Contract, listContractsByGroupSlug } from 'web/lib/firebase/contracts' import { addContractToGroup, @@ -30,7 +29,7 @@ import Custom404 from '../../404' import { SEO } from 'web/components/SEO' import { Linkify } from 'web/components/linkify' import { fromPropz, usePropz } from 'web/hooks/use-propz' -import { Tabs } from 'web/components/layout/tabs' + import { ChoicesToggleGroup } from 'web/components/choices-toggle-group' import { ContractSearch } from 'web/components/contract-search' import { JoinOrLeaveGroupButton } from 'web/components/groups/groups-button' @@ -49,6 +48,9 @@ import { Spacer } from 'web/components/layout/spacer' import { usePost } from 'web/hooks/use-post' import { useAdmin } from 'web/hooks/use-admin' import { track } from '@amplitude/analytics-browser' +import { GroupNavBar } from 'web/components/nav/group-nav-bar' +import { ArrowLeftIcon } from '@heroicons/react/solid' +import { GroupSidebar } from 'web/components/nav/group-sidebar' import { SelectMarketsModal } from 'web/components/contract-select-modal' import { BETTORS } from 'common/user' @@ -138,6 +140,7 @@ export default function GroupPage(props: { const user = useUser() const isAdmin = useAdmin() const memberIds = useMemberIds(group?.id ?? null) ?? props.memberIds + const [sidebarIndex, setSidebarIndex] = useState(0) useSaveReferral(user, { defaultReferrerUsername: creator.username, @@ -151,7 +154,7 @@ export default function GroupPage(props: { const isMember = user && memberIds.includes(user.id) const maxLeaderboardSize = 50 - const leaderboard = ( + const leaderboardPage = (
) - const aboutTab = ( + const aboutPage = ( {(group.aboutPostId != null || isCreator || isAdmin) && ( ) - const questionsTab = ( - + const questionsPage = ( + <> + {/* align the divs to the right */} +
+
+ +
+
+ + ) - const tabs = [ + const sidebarPages = [ { title: 'Markets', - content: questionsTab, + content: questionsPage, href: groupPath(group.slug, 'markets'), + key: 'markets', }, { title: 'Leaderboards', - content: leaderboard, + content: leaderboardPage, href: groupPath(group.slug, 'leaderboards'), + key: 'leaderboards', }, { title: 'About', - content: aboutTab, + content: aboutPage, href: groupPath(group.slug, 'about'), + key: 'about', }, ] - const tabIndex = tabs - .map((t) => t.title.toLowerCase()) - .indexOf(page ?? 'markets') + const pageContent = sidebarPages[sidebarIndex].content + const onSidebarClick = (key: string) => { + const index = sidebarPages.findIndex((t) => t.key === key) + setSidebarIndex(index) + } + + const joinOrAddQuestionsButton = ( + + ) return ( - - - - -
-
- {group.name} -
-
- -
-
-
- -
-
- - 0 ? tabIndex : 0} - tabs={tabs} - /> -
+ <> + +
+
+ + + + +
+ {pageContent} +
+
+ +
+ + ) +} + +export function TopGroupNavBar(props: { group: Group }) { + return ( +
+
+
+ + + + +
+
+

+ {props.group.name} +

+
+
+
) } @@ -264,10 +312,11 @@ function JoinOrAddQuestionsButtons(props: { group: Group user: User | null | undefined isMember: boolean + className?: string }) { const { group, user, isMember } = props return user && isMember ? ( - + ) : group.anyoneCanJoin ? ( @@ -411,9 +460,9 @@ function AddContractButton(props: { group: Group; user: User }) { return ( <> -
+
diff --git a/web/pages/home.tsx b/web/pages/home.tsx index 972aa639..50e2c35f 100644 --- a/web/pages/home.tsx +++ b/web/pages/home.tsx @@ -26,6 +26,7 @@ const Home = () => { user={user} persistPrefix="home-search" useQueryUrlParam={true} + headerClassName="sticky" />