interface: inject hooks to track position of tutorial elements

This commit is contained in:
Liam Fitzgerald 2021-02-08 10:36:06 +10:00
parent 44debd2936
commit 1e7a09714b
No known key found for this signature in database
GPG Key ID: D390E12C61D1CFFB
13 changed files with 104 additions and 95 deletions

View File

@ -55,7 +55,6 @@ const edit = (json: ContactUpdate, state: S) => {
data &&
(ship in state.contacts)
) {
console.log(data);
const [field] = Object.keys(data['edit-field']);
if (!field) {
return;

View File

@ -36,7 +36,6 @@ export function ChatResource(props: ChatResourceProps) {
const [,, owner, name] = station.split('/');
const ourContact = contacts?.[`~${window.ship}`];
console.log(contacts);
const chatInput = useRef<ChatInput>();

View File

@ -1,4 +1,4 @@
import React from "react";
import React, {useRef} from "react";
import { Box, Text, Col } from "@tlon/indigo-react";
import f from "lodash/fp";
import _ from "lodash";
@ -7,6 +7,7 @@ import { Associations, Association, Unreads, UnreadStats } from "~/types";
import { alphabeticalOrder } from "~/logic/lib/util";
import { getUnreadCount, getNotificationCount } from "~/logic/lib/hark";
import Tile from "../components/tiles/tile";
import { useTutorialModal } from "~/views/components/useTutorialModal";
interface GroupsProps {
associations: Associations;
@ -73,8 +74,15 @@ interface GroupProps {
}
function Group(props: GroupProps) {
const { path, title, unreads, updates, first = false } = props;
const anchorRef = useRef<HTMLElement>(null);
const isTutorialGroup = path === '/ship/~hastuc-dibtux/beginner-island';
useTutorialModal(
'start',
isTutorialGroup,
anchorRef.current
);
return (
<Tile to={`/~landscape${path}`} gridColumnStart={first ? '1' : null}>
<Tile ref={anchorRef} position="relative" bg={isTutorialGroup ? 'lightBlue' : undefined} to={`/~landscape${path}`} gridColumnStart={first ? '1' : null}>
<Col height="100%" justifyContent="space-between">
<Text>{title}</Text>
<Col>

View File

@ -22,43 +22,45 @@ const SquareBox = styled(Box)`
`;
const routeList = defaultApps.map(a => `/~${a}`);
export default class Tile extends React.Component {
render() {
const { bg, to, href, p, boxShadow, gridColumnStart, ...props } = this.props;
const Tile = React.forwardRef((props, ref) => {
const { bg, to, href, p, boxShadow, gridColumnStart, ...rest } = props;
let childElement = (
<Box p={typeof p === 'undefined' ? 2 : p} width="100%" height="100%">
{props.children}
</Box>
);
if (to) {
if (routeList.indexOf(to) !== -1 || to === '/~profile' || to.startsWith('/~landscape/')) {
childElement= (<Link to={to}>{childElement}</Link>);
} else {
childElement= (<a href={to}>{childElement}</a>);
}
let childElement = (
<Box p={typeof p === 'undefined' ? 2 : p} width="100%" height="100%">
{props.children}
</Box>
);
if (to) {
if (routeList.indexOf(to) !== -1 || to === '/~profile' || to.startsWith('/~landscape/')) {
childElement= (<Link to={to}>{childElement}</Link>);
} else {
childElement= (<a href={to}>{childElement}</a>);
}
return (
<SquareBox
borderRadius={2}
overflow="hidden"
bg={bg || "white"}
color={props?.color || 'washedGray'}
boxShadow={boxShadow || '0 0 0px 1px inset'}
style={{ gridColumnStart }}
>
<Box
{...props}
height="100%"
width="100%"
>
{childElement}
</Box>
</SquareBox>
);
}
}
return (
<SquareBox
ref={ref}
position="relative"
borderRadius={2}
overflow="hidden"
bg={bg || "white"}
color={props?.color || 'washedGray'}
boxShadow={boxShadow || '0 0 0px 1px inset'}
style={{ gridColumnStart }}
>
<Box
{...rest}
height="100%"
width="100%"
>
{childElement}
</Box>
</SquareBox>
);
});
export default Tile;

View File

@ -1,4 +1,4 @@
import React, {useEffect} from "react";
import React, {useEffect, useRef} from "react";
import { Sigil } from "~/logic/lib/sigil";
import { ViewProfile } from './ViewProfile';
import { EditProfile } from './EditProfile';
@ -15,6 +15,7 @@ import {
} from "@tlon/indigo-react";
import useLocalState from "~/logic/state/local";
import { useHistory } from "react-router-dom";
import {useTutorialModal} from "~/views/components/useTutorialModal";
export function Profile(props: any) {
@ -43,6 +44,10 @@ export function Profile(props: any) {
? <BaseImage src={contact.avatar} width='100%' height='100%' style={{ objectFit: 'cover' }} />
: <Sigil ship={ship} size={96} color={hexColor} />;
const anchorRef = useRef<HTMLElement | null>(null);
useTutorialModal('profile', ship === `~${window.ship}`, anchorRef.current);
return (
<Center
p={4}
@ -55,7 +60,7 @@ export function Profile(props: any) {
<SetStatus ship={ship} contact={contact} api={props.api} />
) : null
}
<Row width="100%" height="300px">
<Row ref={anchorRef} width="100%" height="300px">
{cover}
</Row>
<Row

View File

@ -11,9 +11,7 @@ import { Box, Col } from "@tlon/indigo-react";
import { useOutsideClick } from "~/logic/lib/useOutsideClick";
import { useLocation } from "react-router-dom";
import { Portal } from "./Portal";
type AlignY = "top" | "bottom";
type AlignX = "left" | "right";
import { getRelativePosition, AlignY, AlignX } from "~/logic/lib/relativePosition";
interface DropdownProps {
children: ReactNode;
@ -44,46 +42,11 @@ export function Dropdown(props: DropdownProps) {
const [coords, setCoords] = useState({});
const updatePos = useCallback(() => {
const rect = anchorRef.current?.getBoundingClientRect();
if (rect) {
const bounds = {
top: rect.top,
left: rect.left,
bottom: document.documentElement.clientHeight - rect.bottom,
right: document.documentElement.clientWidth - rect.right,
};
const alignX = _.isArray(props.alignX) ? props.alignX : [props.alignX];
const alignY = _.isArray(props.alignY) ? props.alignY : [props.alignY];
let newCoords = {
..._.reduce(
alignX,
(acc, a, idx) => ({
...acc,
[a]: _.zipWith(
[...Array(idx), `${bounds[a]}px`],
acc[a] || [],
(a, b) => a || b || null
),
}),
{}
),
..._.reduce(
alignY,
(acc, a, idx) => ({
...acc,
[a]: _.zipWith(
[...Array(idx), `${bounds[a]}px`],
acc[a] || [],
(a, b) => a || b || null
),
}),
{}
),
};
const newCoords = getRelativePosition(anchorRef.current, props.alignX, props.alignY);
if(newCoords) {
setCoords(newCoords);
}
}, [setCoords, anchorRef.current]);
}, [setCoords, anchorRef.current, props.alignY, props.alignX]);
useEffect(() => {
if (!open) {

View File

@ -21,12 +21,12 @@ interface HoverBoxLinkProps {
to: string;
}
export const HoverBoxLink = ({
export const HoverBoxLink = React.forwardRef(({
to,
children,
...rest
}: HoverBoxLinkProps & PropFunc<typeof HoverBox>) => (
<Link to={to}>
}: HoverBoxLinkProps & PropFunc<typeof HoverBox>, ref) => (
<Link ref={ref} to={to}>
<HoverBox {...rest}>{children}</HoverBox>
</Link>
);
));

View File

@ -1,6 +1,7 @@
import React, {
useState,
useEffect
useEffect,
useRef
} from 'react';
import {
@ -18,6 +19,7 @@ import { StatusBarItem } from './StatusBarItem';
import { Sigil } from '~/logic/lib/sigil';
import { uxToHex } from "~/logic/lib/util";
import { SetStatusBarModal } from './SetStatusBarModal';
import { useTutorialModal } from './useTutorialModal';
import useLocalState from '~/logic/state/local';
@ -43,6 +45,10 @@ const StatusBar = (props) => {
style={{ objectFit: 'cover' }} />
) : <Sigil ship={ship} size={16} color={color} icon />;
const anchorRef = useRef(null);
useTutorialModal('leap', true, anchorRef.current);
return (
<Box
display='grid'
@ -64,7 +70,7 @@ const StatusBar = (props) => {
</Box>
)}
<Icon icon='LeapArrow'/>
<Text ml={2} color='black'>
<Text ref={anchorRef} ml={2} color='black'>
Leap
</Text>
<Text display={['none', 'inline']} ml={2} color='gray'>

View File

@ -1,8 +1,9 @@
import React from "react";
import React, {useRef} from "react";
import { Col, Text, BaseLabel, Label } from "@tlon/indigo-react";
import GlobalApi from "~/logic/api/global";
import { Association, NotificationGraphConfig } from "~/types";
import { StatelessAsyncToggle } from "~/views/components/StatelessAsyncToggle";
import {useTutorialModal} from "~/views/components/useTutorialModal";
interface ChannelNotificationsProps {
api: GlobalApi;
@ -24,9 +25,13 @@ export function ChannelNotifications(props: ChannelNotificationsProps) {
await api.hark[func](rid, "/");
};
const anchorRef = useRef<HTMLElement | null>(null)
useTutorialModal('notifications', true, anchorRef.current);
return (
<Col mb="6" gapY="4" flexShrink={0}>
<Text id="notifications" fontSize="2" fontWeight="bold">
<Text ref={anchorRef} id="notifications" fontSize="2" fontWeight="bold">
Channel Notifications
</Text>
<BaseLabel display="flex" cursor="pointer">

View File

@ -1,19 +1,27 @@
import React, { ReactNode } from "react";
import React, { ReactNode, useRef } from "react";
import { Metadata } from "~/types";
import { Col, Row, Text } from "@tlon/indigo-react";
import { MetadataIcon } from "./MetadataIcon";
import { useTutorialModal } from "~/views/components/useTutorialModal";
interface GroupSummaryProps {
metadata: Metadata;
memberCount: number;
channelCount: number;
resource?: string;
children?: ReactNode;
}
export function GroupSummary(props: GroupSummaryProps) {
const { channelCount, memberCount, metadata, children } = props;
const { channelCount, memberCount, metadata, resource, children } = props;
const anchorRef = useRef<HTMLElement | null>(null);
useTutorialModal(
"group-desc",
resource === "/ship/~hastuc-dibtux/beginner-island",
anchorRef.current
);
return (
<Col maxWidth="300px" gapY="4">
<Col ref={anchorRef} maxWidth="300px" gapY="4">
<Row gapX="2">
<MetadataIcon
borderRadius="1"

View File

@ -199,6 +199,7 @@ export function GroupsPane(props: GroupsPaneProps) {
memberCount={memberCount}
channelCount={0}
metadata={groupAssociation.metadata}
resource={groupAssociation.group}
/>
} else {
summary = (<Box p="4"><Text fontSize="0" color='gray'>

View File

@ -1,4 +1,4 @@
import React, { ReactNode } from 'react';
import React, { ReactNode, useRef } from 'react';
import styled from 'styled-components';
import {
Col
@ -19,6 +19,7 @@ import { getGroupFromWorkspace } from '~/logic/lib/workspace';
import { SidebarAppConfigs } from './types';
import { SidebarList } from './SidebarList';
import { roleForShip } from '~/logic/lib/group';
import {useTutorialModal} from '~/views/components/useTutorialModal';
const ScrollbarLessCol = styled(Col)`
scrollbar-width: none !important;
@ -64,8 +65,12 @@ export function Sidebar(props: SidebarProps) {
const role = props.groups?.[groupPath] ? roleForShip(props.groups[groupPath], window.ship) : undefined;
const isAdmin = (role === 'admin') || (workspace?.type === 'home');
const anchorRef = useRef<HTMLElement | null>(null);
useTutorialModal('channels', true, anchorRef.current);
return (
<ScrollbarLessCol
ref={anchorRef}
display={display}
width="100%"
gridRow="1/2"

View File

@ -1,4 +1,4 @@
import React from "react";
import React, {useRef} from "react";
import _ from 'lodash';
import { Icon, Row, Box, Text, BaseImage } from "@tlon/indigo-react";
@ -9,6 +9,7 @@ import { Groups, Association } from "~/types";
import { Sigil } from '~/logic/lib/sigil';
import urbitOb from 'urbit-ob';
import { getModuleIcon, getItemTitle, uxToHex } from "~/logic/lib/util";
import {useTutorialModal} from "~/views/components/useTutorialModal";
function SidebarItemIndicator(props: { status?: SidebarItemStatus }) {
switch (props.status) {
@ -41,6 +42,12 @@ export function SidebarItem(props: {
const mod = association?.metadata?.module || appName;
const rid = association?.resource
const groupPath = association?.group;
const anchorRef = useRef<HTMLElement | null>(null)
useTutorialModal(
mod as any,
groupPath === '/ship/~hastuc-dibtux/beginner-island',
anchorRef.current
);
const app = apps[appName];
const isUnmanaged = groups?.[groupPath]?.hidden || false;
if (!app) {
@ -87,6 +94,7 @@ export function SidebarItem(props: {
return (
<HoverBoxLink
ref={anchorRef}
to={to}
bg="white"
bgActive="washedGray"