mirror of
https://github.com/ilyakooo0/urbit.git
synced 2024-09-21 15:38:59 +03:00
interface: refactor sidebar, improve routing
This commit is contained in:
parent
c00768442a
commit
c90f7bde25
@ -1,6 +1,7 @@
|
||||
import dark from '@tlon/indigo-dark';
|
||||
import light from '@tlon/indigo-light';
|
||||
import Mousetrap from 'mousetrap';
|
||||
import shallow from 'zustand/shallow';
|
||||
import 'mousetrap-global-bind';
|
||||
import * as React from 'react';
|
||||
import Helmet from 'react-helmet';
|
||||
@ -12,7 +13,6 @@ import gcpManager from '~/logic/lib/gcpManager';
|
||||
import { favicon, svgDataURL } from '~/logic/lib/util';
|
||||
import withState from '~/logic/lib/withState';
|
||||
import useContactState from '~/logic/state/contact';
|
||||
import useGroupState from '~/logic/state/group';
|
||||
import useLocalState from '~/logic/state/local';
|
||||
import useSettingsState from '~/logic/state/settings';
|
||||
import useGraphState from '~/logic/state/graph';
|
||||
@ -131,7 +131,7 @@ class App extends React.Component {
|
||||
render() {
|
||||
const theme = this.getTheme();
|
||||
|
||||
const ourContact = this.props.contacts[`~${this.ship}`] || null;
|
||||
const { ourContact } = this.props;
|
||||
return (
|
||||
<ThemeProvider theme={theme}>
|
||||
<ShortcutContextProvider>
|
||||
@ -173,12 +173,38 @@ class App extends React.Component {
|
||||
);
|
||||
}
|
||||
}
|
||||
const WarmApp = process.env.NODE_ENV === 'production' ? App : hot(App);
|
||||
|
||||
const selContacts = s => s.contacts[`~${window.ship}`];
|
||||
const selLocal = s => [s.set, s.omniboxShown, s.toggleOmnibox];
|
||||
const selSettings = s => [s.display, s.getAll];
|
||||
const selGraph = s => s.getShallowChildren;
|
||||
const selLaunch = s => [s.getRuntimeLag, s.getBaseHash];
|
||||
|
||||
const WithApp = React.forwardRef((props, ref) => {
|
||||
const ourContact = useContactState(selContacts);
|
||||
const [display, getAll] = useSettingsState(selSettings, shallow);
|
||||
const [setLocal, omniboxShown, toggleOmnibox] = useLocalState(selLocal);
|
||||
const getShallowChildren = useGraphState(selGraph);
|
||||
const [getRuntimeLag, getBaseHash] = useLaunchState(selLaunch, shallow);
|
||||
|
||||
return (
|
||||
<WarmApp
|
||||
ref={ref}
|
||||
ourContact={ourContact}
|
||||
display={display}
|
||||
getAll={getAll}
|
||||
set={setLocal}
|
||||
getShallowChildren={getShallowChildren}
|
||||
getRuntimeLag={getRuntimeLag}
|
||||
getBaseHash={getBaseHash}
|
||||
toggleOmnibox={toggleOmnibox}
|
||||
omniboxShown={omniboxShown}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
WarmApp.whyDidYouRender = true;
|
||||
|
||||
export default WithApp;
|
||||
|
||||
export default withState(process.env.NODE_ENV === 'production' ? App : hot(App), [
|
||||
[useGroupState],
|
||||
[useContactState],
|
||||
[useSettingsState, ['display', 'getAll']],
|
||||
[useLocalState],
|
||||
[useGraphState, ['getShallowChildren']],
|
||||
[useLaunchState, ['getRuntimeLag', 'getBaseHash']]
|
||||
]);
|
||||
|
@ -37,7 +37,6 @@ export const Content = (props) => {
|
||||
history.goBack();
|
||||
}, [history.goBack]));
|
||||
|
||||
|
||||
const [hasProtocol, setHasProtocol] = useLocalStorageState(
|
||||
'registeredProtocol', false
|
||||
);
|
||||
@ -78,16 +77,9 @@ export const Content = (props) => {
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Route
|
||||
path='/~landscape'
|
||||
render={p => (
|
||||
<Landscape
|
||||
location={p.location}
|
||||
match={p.match}
|
||||
{...props}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Route path='/~landscape'>
|
||||
<Landscape />
|
||||
</Route>
|
||||
<Route
|
||||
path="/~profile"
|
||||
render={ p => (
|
||||
|
@ -121,7 +121,6 @@ function stitchInline(a: any, b: any) {
|
||||
}
|
||||
const lastParaIdx = a.children.length - 1;
|
||||
const last = a.children[lastParaIdx];
|
||||
console.log(last);
|
||||
if (last?.children) {
|
||||
const ros = {
|
||||
...a,
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { AppName, readGroup } from '@urbit/api';
|
||||
import { readGroup } from '@urbit/api';
|
||||
import _ from 'lodash';
|
||||
import React, { useCallback, useEffect } from 'react';
|
||||
import Helmet from 'react-helmet';
|
||||
@ -27,10 +27,10 @@ import { Resource } from './Resource';
|
||||
import { Skeleton } from './Skeleton';
|
||||
import airlock from '~/logic/api';
|
||||
|
||||
type GroupsPaneProps = {
|
||||
interface GroupsPaneProps {
|
||||
baseUrl: string;
|
||||
workspace: Workspace;
|
||||
};
|
||||
}
|
||||
|
||||
export function GroupsPane(props: GroupsPaneProps) {
|
||||
const { baseUrl, workspace } = props;
|
||||
@ -114,8 +114,6 @@ export function GroupsPane(props: GroupsPaneProps) {
|
||||
string
|
||||
>;
|
||||
|
||||
const appName = app as AppName;
|
||||
|
||||
const resource = `/ship/${host}/${name}`;
|
||||
const association = associations.graph[resource];
|
||||
const resourceUrl = `${baseUrl}/resource/${app}${resource}`;
|
||||
@ -129,12 +127,11 @@ export function GroupsPane(props: GroupsPaneProps) {
|
||||
mobileHide
|
||||
recentGroups={recentGroups}
|
||||
selected={resource}
|
||||
selectedApp={appName}
|
||||
{...props}
|
||||
baseUrl={resourceUrl}
|
||||
>
|
||||
<Resource
|
||||
{...props}
|
||||
workspace={props.workspace}
|
||||
association={association}
|
||||
baseUrl={baseUrl}
|
||||
/>
|
||||
|
@ -13,11 +13,11 @@ import { PublishResource } from '~/views/apps/publish/PublishResource';
|
||||
import { ChannelPopoverRoutes } from './ChannelPopoverRoutes';
|
||||
import { ResourceSkeleton } from './ResourceSkeleton';
|
||||
|
||||
type ResourceProps = {
|
||||
interface ResourceProps {
|
||||
association: Association;
|
||||
baseUrl: string;
|
||||
workspace: Workspace;
|
||||
};
|
||||
}
|
||||
|
||||
export function Resource(props: ResourceProps): ReactElement {
|
||||
const { association } = props;
|
||||
|
@ -106,7 +106,7 @@ export function ResourceSkeleton(props: ResourceSkeletonProps): ReactElement {
|
||||
canWrite = isOwn;
|
||||
}
|
||||
|
||||
const BackLink = () => (
|
||||
const backLink = (
|
||||
<Box
|
||||
borderRight={1}
|
||||
borderRightColor='gray'
|
||||
@ -123,7 +123,7 @@ export function ResourceSkeleton(props: ResourceSkeletonProps): ReactElement {
|
||||
</Box>
|
||||
);
|
||||
|
||||
const Title = () => (
|
||||
const titleText = (
|
||||
<Text
|
||||
mono={urbitOb.isValidPatp(title)}
|
||||
fontSize={2}
|
||||
@ -143,7 +143,7 @@ export function ResourceSkeleton(props: ResourceSkeletonProps): ReactElement {
|
||||
</Text>
|
||||
);
|
||||
|
||||
const Description = () => (
|
||||
const description = (
|
||||
<TruncatedText
|
||||
display={['none','inline']}
|
||||
mono={workspace === '/messages' && !association?.metadata?.description}
|
||||
@ -160,9 +160,8 @@ export function ResourceSkeleton(props: ResourceSkeletonProps): ReactElement {
|
||||
</TruncatedText>
|
||||
);
|
||||
|
||||
const ExtraControls = () => {
|
||||
if (workspace === '/messages' && isOwn && !resource.startsWith('dm-')) {
|
||||
return (
|
||||
const extraControls =
|
||||
(workspace === '/messages' && isOwn && !resource.startsWith('dm-')) ? (
|
||||
<Dropdown
|
||||
flexShrink={0}
|
||||
dropWidth='300px'
|
||||
@ -186,21 +185,15 @@ export function ResourceSkeleton(props: ResourceSkeletonProps): ReactElement {
|
||||
+ Add Ship
|
||||
</Text>
|
||||
</Dropdown>
|
||||
);
|
||||
}
|
||||
if (canWrite) {
|
||||
return (
|
||||
) : canWrite ? (
|
||||
<Link to={resourcePath('/new')}>
|
||||
<Text bold pr='3' color='blue'>
|
||||
+ New Post
|
||||
</Text>
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
) : null;
|
||||
|
||||
const MenuControl = () => (
|
||||
const menuControl = (
|
||||
<Link to={`${baseUrl}/settings`}>
|
||||
<Icon icon='Menu' color='gray' pr={2} />
|
||||
</Link>
|
||||
@ -229,9 +222,9 @@ export function ResourceSkeleton(props: ResourceSkeletonProps): ReactElement {
|
||||
width={`calc(100% - ${actionsWidth}px - 16px)`}
|
||||
flexShrink={0}
|
||||
>
|
||||
<BackLink />
|
||||
<Title />
|
||||
<Description />
|
||||
{backLink}
|
||||
{titleText}
|
||||
{description}
|
||||
</Box>
|
||||
<Box
|
||||
ml={3}
|
||||
@ -240,8 +233,8 @@ export function ResourceSkeleton(props: ResourceSkeletonProps): ReactElement {
|
||||
flexShrink={0}
|
||||
ref={actionsRef}
|
||||
>
|
||||
{ExtraControls()}
|
||||
<MenuControl />
|
||||
{extraControls}
|
||||
{menuControl}
|
||||
</Box>
|
||||
</Box>
|
||||
{children}
|
||||
|
@ -12,7 +12,7 @@ import { useTutorialModal } from '~/views/components/useTutorialModal';
|
||||
import { GroupSwitcher } from '../GroupSwitcher';
|
||||
import { SidebarList } from './SidebarList';
|
||||
import { SidebarListHeader } from './SidebarListHeader';
|
||||
import { SidebarAppConfigs, SidebarListConfig } from './types';
|
||||
import { SidebarListConfig } from './types';
|
||||
|
||||
const ScrollbarLessCol = styled(Col)`
|
||||
scrollbar-width: none !important;
|
||||
@ -25,8 +25,6 @@ const ScrollbarLessCol = styled(Col)`
|
||||
interface SidebarProps {
|
||||
recentGroups: string[];
|
||||
selected?: string;
|
||||
selectedGroup?: string;
|
||||
apps: SidebarAppConfigs;
|
||||
baseUrl: string;
|
||||
mobileHide?: boolean;
|
||||
workspace: Workspace;
|
||||
@ -84,7 +82,6 @@ export function Sidebar(props: SidebarProps): ReactElement | null {
|
||||
config={config}
|
||||
selected={selected}
|
||||
group={groupPath}
|
||||
apps={props.apps}
|
||||
baseUrl={props.baseUrl}
|
||||
workspace={workspace}
|
||||
/>
|
||||
|
@ -12,9 +12,30 @@ import useContactState, { useContact } from '~/logic/state/contact';
|
||||
import { getItemTitle, getModuleIcon, uxToHex } from '~/logic/lib/util';
|
||||
import useGroupState from '~/logic/state/group';
|
||||
import Dot from '~/views/components/Dot';
|
||||
import { SidebarAppConfigs } from './types';
|
||||
import { useHarkDm } from '~/logic/state/hark';
|
||||
import useHarkState, { useHarkDm } from '~/logic/state/hark';
|
||||
import useSettingsState from '~/logic/state/settings';
|
||||
import useGraphState from '~/logic/state/graph';
|
||||
|
||||
function useAssociationStatus(resource: string) {
|
||||
const [, , ship, name] = resource.split('/');
|
||||
const graphKey = `${ship.slice(1)}/${name}`;
|
||||
const isSubscribed = useGraphState(s => s.graphKeys.has(graphKey));
|
||||
const { unreads, notifications } = useHarkState(
|
||||
s => s.unreads.graph?.[resource]?.['/'] || { unreads: 0, notifications: 0, last: 0 }
|
||||
);
|
||||
const hasNotifications =
|
||||
(typeof notifications === 'number' && notifications > 0) ||
|
||||
(typeof notifications === 'object' && notifications.length);
|
||||
const hasUnread =
|
||||
typeof unreads === 'number' ? unreads > 0 : unreads?.size ?? 0 > 0;
|
||||
return hasNotifications
|
||||
? 'notification'
|
||||
: hasUnread
|
||||
? 'unread'
|
||||
: isSubscribed
|
||||
? undefined
|
||||
: 'unsubscribed';
|
||||
}
|
||||
|
||||
function SidebarItemBase(props: {
|
||||
to: string;
|
||||
@ -36,7 +57,11 @@ function SidebarItemBase(props: {
|
||||
isSynced = false,
|
||||
mono = false
|
||||
} = props;
|
||||
const color = isSynced ? (hasUnread || hasNotification) ? 'black' : 'gray' : 'lightGray';
|
||||
const color = isSynced
|
||||
? hasUnread || hasNotification
|
||||
? 'black'
|
||||
: 'gray'
|
||||
: 'lightGray';
|
||||
|
||||
const fontWeight = hasUnread || hasNotification ? '500' : 'normal';
|
||||
|
||||
@ -95,17 +120,18 @@ function SidebarItemBase(props: {
|
||||
);
|
||||
}
|
||||
|
||||
export function SidebarDmItem(props: {
|
||||
export const SidebarDmItem = React.memo((props: {
|
||||
ship: string;
|
||||
selected?: boolean;
|
||||
workspace: Workspace;
|
||||
}) {
|
||||
}) => {
|
||||
const { ship, selected = false } = props;
|
||||
const contact = useContact(ship);
|
||||
const { hideAvatars, hideNicknames } = useSettingsState(s => s.calm);
|
||||
const title = (!hideNicknames && contact?.nickname)
|
||||
const title =
|
||||
!hideNicknames && contact?.nickname
|
||||
? contact?.nickname
|
||||
: (cite(ship) ?? ship);
|
||||
: cite(ship) ?? ship;
|
||||
const { unreads } = useHarkDm(ship) || { unreads: 0 };
|
||||
const img =
|
||||
contact?.avatar && !hideAvatars ? (
|
||||
@ -139,17 +165,15 @@ export function SidebarDmItem(props: {
|
||||
{img}
|
||||
</SidebarItemBase>
|
||||
);
|
||||
}
|
||||
});
|
||||
// eslint-disable-next-line max-lines-per-function
|
||||
export function SidebarAssociationItem(props: {
|
||||
export const SidebarAssociationItem = React.memo((props: {
|
||||
hideUnjoined: boolean;
|
||||
association: Association;
|
||||
path: string;
|
||||
selected: boolean;
|
||||
apps: SidebarAppConfigs;
|
||||
workspace: Workspace;
|
||||
}) {
|
||||
const { association, path, selected, apps } = props;
|
||||
}) => {
|
||||
const { association, selected } = props;
|
||||
const title = getItemTitle(association) || '';
|
||||
const appName = association?.['app-name'];
|
||||
let mod = appName;
|
||||
@ -158,7 +182,7 @@ export function SidebarAssociationItem(props: {
|
||||
}
|
||||
const rid = association?.resource;
|
||||
const groupPath = association?.group;
|
||||
const groups = useGroupState(state => state.groups);
|
||||
const group = useGroupState(state => state.groups[groupPath]);
|
||||
const { hideNicknames } = useSettingsState(s => s.calm);
|
||||
const contacts = useContactState(s => s.contacts);
|
||||
const anchorRef = useRef<HTMLAnchorElement>(null);
|
||||
@ -167,13 +191,9 @@ export function SidebarAssociationItem(props: {
|
||||
groupPath === `/ship/${TUTORIAL_HOST}/${TUTORIAL_GROUP}`,
|
||||
anchorRef
|
||||
);
|
||||
const app = apps[appName];
|
||||
const isUnmanaged = groups?.[groupPath]?.hidden || false;
|
||||
if (!app) {
|
||||
return null;
|
||||
}
|
||||
const isUnmanaged = group?.hidden || false;
|
||||
const DM = isUnmanaged && props.workspace?.type === 'messages';
|
||||
const itemStatus = app.getStatus(path);
|
||||
const itemStatus = useAssociationStatus(rid);
|
||||
const hasNotification = itemStatus === 'notification';
|
||||
const hasUnread = itemStatus === 'unread';
|
||||
const isSynced = itemStatus !== 'unsubscribed';
|
||||
@ -194,7 +214,11 @@ export function SidebarAssociationItem(props: {
|
||||
}
|
||||
|
||||
const participantNames = (str: string) => {
|
||||
const color = isSynced ? (hasUnread || hasNotification) ? 'black' : 'gray' : 'lightGray';
|
||||
const color = isSynced
|
||||
? hasUnread || hasNotification
|
||||
? 'black'
|
||||
: 'gray'
|
||||
: 'lightGray';
|
||||
if (_.includes(str, ',') && _.startsWith(str, '~')) {
|
||||
const names = _.split(str, ', ');
|
||||
return names.map((name, idx) => {
|
||||
@ -209,9 +233,7 @@ export function SidebarAssociationItem(props: {
|
||||
return (
|
||||
<Text key={name} mono bold={hasUnread} color={color}>
|
||||
{name}
|
||||
<Text color={color}>
|
||||
{idx + 1 != names.length ? ', ' : null}
|
||||
</Text>
|
||||
<Text color={color}>{idx + 1 != names.length ? ', ' : null}</Text>
|
||||
</Text>
|
||||
);
|
||||
} else {
|
||||
@ -230,9 +252,7 @@ export function SidebarAssociationItem(props: {
|
||||
hasUnread={hasUnread}
|
||||
isSynced={isSynced}
|
||||
title={
|
||||
DM && !urbitOb.isValidPatp(title)
|
||||
? participantNames(title)
|
||||
: title
|
||||
DM && !urbitOb.isValidPatp(title) ? participantNames(title) : title
|
||||
}
|
||||
hasNotification={hasNotification}
|
||||
>
|
||||
@ -255,4 +275,4 @@ export function SidebarAssociationItem(props: {
|
||||
)}
|
||||
</SidebarItemBase>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
@ -6,7 +6,7 @@ import { SidebarAssociationItem, SidebarDmItem } from './SidebarItem';
|
||||
import useGraphState, { useInbox } from '~/logic/state/graph';
|
||||
import useHarkState from '~/logic/state/hark';
|
||||
import { alphabeticalOrder, getResourcePath, modulo } from '~/logic/lib/util';
|
||||
import { SidebarAppConfigs, SidebarListConfig, SidebarSort } from './types';
|
||||
import { SidebarListConfig, SidebarSort } from './types';
|
||||
import { Workspace } from '~/types/workspace';
|
||||
import useMetadataState from '~/logic/state/metadata';
|
||||
import { useHistory } from 'react-router';
|
||||
@ -14,8 +14,7 @@ import { useShortcut } from '~/logic/state/settings';
|
||||
|
||||
function sidebarSort(
|
||||
associations: AppAssociations,
|
||||
apps: SidebarAppConfigs,
|
||||
inboxUnreads: Record<string, UnreadStats>
|
||||
unreads: Record<string, Record<string, UnreadStats>>
|
||||
): Record<SidebarSort, (a: string, b: string) => number> {
|
||||
const alphabetical = (a: string, b: string) => {
|
||||
const aAssoc = associations[a];
|
||||
@ -29,16 +28,16 @@ function sidebarSort(
|
||||
const lastUpdated = (a: string, b: string) => {
|
||||
const aAssoc = associations[a];
|
||||
const bAssoc = associations[b];
|
||||
const aAppName = aAssoc?.['app-name'];
|
||||
const bAppName = bAssoc?.['app-name'];
|
||||
const aResource = aAssoc.resource;
|
||||
const bResource = bAssoc.resource;
|
||||
|
||||
const aUpdated = a.startsWith('~')
|
||||
? (inboxUnreads?.[`/${patp2dec(a)}`]?.last || 0)
|
||||
: (apps[aAppName]?.lastUpdated(a) || 0);
|
||||
? (unreads?.[`/ship/~${window.ship}/dm-inbox`]?.[`/${patp2dec(a)}`]?.last || 0)
|
||||
: ((unreads?.[aResource]?.['/']?.last) || 0);
|
||||
|
||||
const bUpdated = b.startsWith('~')
|
||||
? (inboxUnreads?.[`/${patp2dec(b)}`]?.last || 0)
|
||||
: (apps[bAppName]?.lastUpdated(b) || 0);
|
||||
? (unreads?.[`/ship/~${window.ship}/dm-inbox`]?.[`/${patp2dec(b)}`]?.last || 0)
|
||||
: ((unreads?.[bResource]?.['/']?.last) || 0);
|
||||
|
||||
return bUpdated - aUpdated || alphabetical(a, b);
|
||||
};
|
||||
@ -86,7 +85,6 @@ function getItems(associations: Associations, workspace: Workspace, inbox: Graph
|
||||
}
|
||||
|
||||
export function SidebarList(props: {
|
||||
apps: SidebarAppConfigs;
|
||||
config: SidebarListConfig;
|
||||
baseUrl: string;
|
||||
group?: string;
|
||||
@ -96,11 +94,11 @@ export function SidebarList(props: {
|
||||
const { selected, config, workspace } = props;
|
||||
const associations = useMetadataState(state => state.associations);
|
||||
const inbox = useInbox();
|
||||
const unreads = useHarkState(s => s.unreads.graph?.[`/ship/~${window.ship}/dm-inbox`]);
|
||||
const unreads = useHarkState(s => s.unreads.graph);
|
||||
const graphKeys = useGraphState(s => s.graphKeys);
|
||||
|
||||
const ordered = getItems(associations, workspace, inbox)
|
||||
.sort(sidebarSort(associations.graph, props.apps, unreads)[config.sortBy]);
|
||||
.sort(sidebarSort(associations.graph, unreads)[config.sortBy]);
|
||||
|
||||
const history = useHistory();
|
||||
|
||||
@ -139,10 +137,8 @@ export function SidebarList(props: {
|
||||
return pathOrShip.startsWith('/') ? (
|
||||
<SidebarAssociationItem
|
||||
key={pathOrShip}
|
||||
path={pathOrShip}
|
||||
selected={pathOrShip === selected}
|
||||
association={associations.graph[pathOrShip]}
|
||||
apps={props.apps}
|
||||
hideUnjoined={config.hideUnjoined}
|
||||
workspace={workspace}
|
||||
/>
|
||||
|
@ -1,39 +1,24 @@
|
||||
import React, { ReactElement, ReactNode, useCallback, useMemo, useState } from 'react';
|
||||
import React, { ReactElement, ReactNode, useCallback, useState } from 'react';
|
||||
import { Sidebar } from './Sidebar/Sidebar';
|
||||
import { AppName } from '@urbit/api';
|
||||
import useGraphState from '~/logic/state/graph';
|
||||
import useHarkState from '~/logic/state/hark';
|
||||
import { Workspace } from '~/types/workspace';
|
||||
import { Body } from '~/views/components/Body';
|
||||
import ErrorBoundary from '~/views/components/ErrorBoundary';
|
||||
import { useShortcut } from '~/logic/state/settings';
|
||||
import { useGraphModule } from './Sidebar/Apps';
|
||||
|
||||
interface SkeletonProps {
|
||||
children: ReactNode;
|
||||
recentGroups: string[];
|
||||
selected?: string;
|
||||
selectedApp?: AppName;
|
||||
baseUrl: string;
|
||||
mobileHide?: boolean;
|
||||
workspace: Workspace;
|
||||
}
|
||||
|
||||
export function Skeleton(props: SkeletonProps): ReactElement {
|
||||
export const Skeleton = React.memo((props: SkeletonProps): ReactElement => {
|
||||
const [sidebar, setSidebar] = useState(true);
|
||||
useShortcut('hideSidebar', useCallback(() => {
|
||||
setSidebar(s => !s);
|
||||
}, []));
|
||||
const graphs = useGraphState(state => state.graphs);
|
||||
const graphKeys = useGraphState(state => state.graphKeys);
|
||||
const unreads = useHarkState(state => state.unreads);
|
||||
const graphConfig = useGraphModule(graphKeys, graphs, unreads.graph);
|
||||
const config = useMemo(
|
||||
() => ({
|
||||
graph: graphConfig
|
||||
}),
|
||||
[graphConfig]
|
||||
);
|
||||
|
||||
return !sidebar ? (<Body> {props.children} </Body>) : (
|
||||
<Body
|
||||
@ -47,7 +32,6 @@ export function Skeleton(props: SkeletonProps): ReactElement {
|
||||
<Sidebar
|
||||
recentGroups={props.recentGroups}
|
||||
selected={props.selected}
|
||||
apps={config}
|
||||
baseUrl={props.baseUrl}
|
||||
mobileHide={props.mobileHide}
|
||||
workspace={props.workspace}
|
||||
@ -56,4 +40,4 @@ export function Skeleton(props: SkeletonProps): ReactElement {
|
||||
{props.children}
|
||||
</Body>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
@ -10,6 +10,7 @@ import { GroupsPane } from './components/GroupsPane';
|
||||
import { JoinGroup } from './components/JoinGroup';
|
||||
import { NewGroup } from './components/NewGroup';
|
||||
import './css/custom.css';
|
||||
import _ from 'lodash';
|
||||
|
||||
moment.updateLocale('en', {
|
||||
relativeTime : {
|
||||
@ -32,7 +33,12 @@ moment.updateLocale('en', {
|
||||
}
|
||||
});
|
||||
|
||||
export default function Landscape(props) {
|
||||
const makeGroupWorkspace = _.memoize((group: string): Workspace => ({ type: 'group', group }));
|
||||
|
||||
const homeWorkspace: Workspace = { type: 'home' };
|
||||
const messagesWorkspace: Workspace = { type: 'messages' };
|
||||
|
||||
export default function Landscape() {
|
||||
const notificationsCount = useHarkState(s => s.notificationsCount);
|
||||
|
||||
return (
|
||||
@ -49,40 +55,26 @@ export default function Landscape(props) {
|
||||
} = routeProps.match.params as Record<string, string>;
|
||||
const groupPath = `/ship/${host}/${name}`;
|
||||
const baseUrl = `/~landscape${groupPath}`;
|
||||
const ws: Workspace = { type: 'group', group: groupPath };
|
||||
const ws: Workspace = makeGroupWorkspace(groupPath);
|
||||
|
||||
return (
|
||||
<GroupsPane workspace={ws} baseUrl={baseUrl} {...props} />
|
||||
<GroupsPane workspace={ws} baseUrl={baseUrl} />
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<Route path="/~landscape/home"
|
||||
render={() => {
|
||||
const ws: Workspace = { type: 'home' };
|
||||
return (
|
||||
<GroupsPane workspace={ws} baseUrl="/~landscape/home" {...props} />
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<Route path="/~landscape/messages"
|
||||
render={() => {
|
||||
const ws: Workspace = { type: 'messages' };
|
||||
return (
|
||||
<GroupsPane workspace={ws} baseUrl="/~landscape/messages" {...props} />
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<Route path="/~landscape/new"
|
||||
render={() => {
|
||||
return (
|
||||
<Route path="/~landscape/home">
|
||||
<GroupsPane workspace={homeWorkspace} baseUrl="/~landscape/home" />
|
||||
</Route>
|
||||
<Route path="/~landscape/messages">
|
||||
<GroupsPane workspace={messagesWorkspace} baseUrl="/~landscape/messages" />
|
||||
</Route>
|
||||
<Route path="/~landscape/new">
|
||||
<Body>
|
||||
<Box maxWidth="300px">
|
||||
<NewGroup />
|
||||
</Box>
|
||||
</Body>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</Route>
|
||||
<Route path="/~landscape/join/:ship?/:name?"
|
||||
render={(routeProps) => {
|
||||
const { ship, name } = routeProps.match.params;
|
||||
@ -103,4 +95,3 @@ export default function Landscape(props) {
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user