interface: fix iOS scrolling

This commit is contained in:
Liam Fitzgerald 2020-10-07 15:46:43 +10:00
parent 7a90f04a06
commit 60cf95f3da
20 changed files with 152 additions and 92 deletions

Binary file not shown.

View File

@ -9,7 +9,7 @@
"@reach/menu-button": "^0.10.5",
"@reach/tabs": "^0.10.5",
"@tlon/indigo-light": "^1.0.3",
"@tlon/indigo-react": "^1.2.8",
"@tlon/indigo-react": "urbit/indigo-react#lf/1.2.9",
"@tlon/sigil-js": "^1.4.2",
"aws-sdk": "^2.726.0",
"classnames": "^2.2.6",

View File

@ -0,0 +1,33 @@
import { MouseEvent, useCallback, useState, useEffect } from "react";
export type AsyncClickableState = "waiting" | "error" | "loading" | "success";
export function useStatelessAsyncClickable(
onClick: (e: MouseEvent) => Promise<void>,
name: string
) {
const [state, setState] = useState<ButtonState>("waiting");
const handleClick = useCallback(
async (e: MouseEvent) => {
try {
setState("loading");
await onClick(e);
setState("success");
} catch (e) {
console.error(e);
setState("error");
} finally {
setTimeout(() => {
setState("waiting");
}, 3000);
}
},
[onClick, setState]
);
// When name changes, reset button
useEffect(() => {
setState("waiting");
}, [name]);
return { buttonState: state, onClick: handleClick };
}

View File

@ -133,10 +133,10 @@ export default class ChatEditor extends Component {
return (
<div
className={
'chat fr h-100 flex bg-gray0-d lh-copy w-100 items-center ' +
'chat fr flex h-100 bg-gray0-d lh-copy w-100 items-center ' +
(inCodeMode ? ' code' : '')
}
style={{ flexGrow: 1, paddingBottom: '3px', maxHeight: '224px', width: 'calc(100% - 88px)' }}>
style={{ flexGrow: 1, paddingBottom: '2px', maxHeight: '224px', width: 'calc(100% - 88px)' }}>
<CodeEditor
value={message}
options={options}

View File

@ -1,6 +1,7 @@
* {
-webkit-font-smoothing: antialiased;
-webkit-touch-callout: none;
-webkit-overflow-scrolling: touch;
}
html, body {
@ -8,14 +9,16 @@ html, body {
width: 100%;
margin: 0;
padding: 0;
position: fixed;
overflow: hidden;
}
#root {
height: 100%;
width: 100%;
position: absolute;
margin: 0;
padding: 0;
overflow: hidden;
}
p, h1, h2, h3, h4, h5, h6, a, input, textarea, button {
@ -225,6 +228,7 @@ blockquote {
height: 100% !important;
width: 100% !important;
cursor: text;
background: transparent;
}
.chat .CodeMirror * {
@ -233,7 +237,6 @@ blockquote {
.chat .cm-s-tlon.CodeMirror {
font-size: 16px;
margin-top: 6px;
}
pre, code {
@ -383,7 +386,6 @@ pre.CodeMirror-placeholder.CodeMirror-line-like { color: var(--gray); }
/* codemirror */
.chat .cm-s-tlon.CodeMirror {
background: #333;
color: #fff;
font-size: 16px;
margin-top: 6px;

View File

@ -33,6 +33,9 @@ button {
cursor: pointer;
}
* {
-webkit-user-drag: none;
}
/* stolen from indigo-react reset.css
* TODO: remove and add reset.css properly

View File

@ -53,7 +53,7 @@ export default function ProfileScreen(props: any) {
return null;
}
if (!view && !MOBILE_BROWSER_REGEX.test(window.navigator.userAgent)) {
history.replace("/~profile/settings");
history.replace("/~profile/identity");
}
return (

View File

@ -7,7 +7,7 @@ export function Body(
) {
const { children, ...boxProps } = props;
return (
<Box fontSize={0} px={[0, 3]} pb={[0, 3]} height="calc(100vh - 64px)" width="100%">
<Box fontSize={0} px={[0, 3]} pb={[0, 3]} height="100%" width="100%">
<Box
bg="white"
height="100%"

View File

@ -17,8 +17,8 @@ const StatusBar = (props) => {
width="100%"
gridTemplateRows="30px"
gridTemplateColumns="3fr 1fr"
py={3}
px={3}
py={[2,3]}
px={[2,3]}
>
<Row collapse>
<StatusBarItem mr={2} onClick={() => props.history.push('/')}>

View File

@ -94,7 +94,12 @@ export function UnjoinedResource(props: UnjoinedResourceProps) {
<Box>
<RichText color="gray">{description}</RichText>
</Box>
<StatelessAsyncButton primary width="fit-content" onClick={onJoin}>
<StatelessAsyncButton
name={appPath}
primary
width="fit-content"
onClick={onJoin}
>
Join Channel
</StatelessAsyncButton>
</Col>

View File

@ -261,8 +261,8 @@ export class Omnibox extends Component {
return (
<Box
backgroundColor='scales.black30'
width='100vw'
height='100vh'
width='100%'
height='100%'
position='absolute'
top='0'
right='0'

View File

@ -1,4 +1,5 @@
import React, { Component } from 'react';
import { Box } from '@tlon/indigo-react';
import { Route, Switch } from 'react-router-dom';
import styled from 'styled-components';
@ -9,8 +10,11 @@ import Profile from '~/views/apps/profile/profile';
import ErrorComponent from '~/views/components/Error';
export const Container = styled.div`
height: calc(100% - 45px);
export const Container = styled(Box)`
flex-grow: 1;
overflow: hidden;
width: 100%;
height: calc(100% - 62px);
`;

View File

@ -137,7 +137,7 @@ export function GroupSwitcher(props: {
color="gray"
icon="Gear"
/>
<Text> Settings</Text>
<Text> Group Settings</Text>
</GroupSwitcherItem>
<GroupSwitcherItem bottom to={navTo("/invites")}>
<Icon

View File

@ -29,6 +29,13 @@ import { useHistory, Link } from "react-router-dom";
import { Dropdown } from "~/views/components/Dropdown";
import GlobalApi from "~/logic/api/global";
import { StatelessAsyncAction } from "~/views/components/StatelessAsyncAction";
import styled from "styled-components";
const TruncText = styled(Box)`
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
`;
type Participant = Contact & { patp: string; pending: boolean };
type ParticipantsTabId = "total" | "pending" | "admin";
@ -108,6 +115,8 @@ export function Participants(props: {
[props.group]
);
const ourRole = roleForShip(props.group, window.ship);
const [filter, setFilter] = useState<ParticipantsTabId>("total");
const [search, _setSearch] = useState("");
@ -137,18 +146,27 @@ export function Participants(props: {
[search, filter, participants]
);
// Sticky positioning needs to be disabled on safari due to this bug
// https://bugs.webkit.org/show_bug.cgi?id=210656
// TODO: remove when resolved
const isSafari = useMemo(() => {
const ua = window.navigator.userAgent;
return ua.includes("Safari") && !ua.includes("Chrome");
}, []);
return (
<Col height="100%" overflowY="auto" p={2} position="relative">
<Col height="100%" overflowY="scroll" p={2} position="relative">
<Row
bg="white"
border={1}
borderColor="washedGray"
borderRadius={1}
position="sticky"
position={isSafari ? "static" : "sticky"}
top="0px"
mb={2}
px={2}
zIndex={1}
flexShrink="0"
>
<Row mr="4" flexShrink="0">
<Tab
@ -173,20 +191,8 @@ export function Participants(props: {
/>
</Row>
</Row>
<Box
display="grid"
gridAutoRows={["48px 48px 1px", "48px 1px"]}
gridTemplateColumns={["48px 1fr", "48px 1fr 144px"]}
gridRowGap={2}
alignItems="center"
>
<Row
alignItems="center"
gridColumn={["1 / 3", "1 / 4"]}
bg="washedGray"
borderRadius="1"
px="2"
>
<Col flexShrink="0" width="100%" height="fit-content">
<Row alignItems="center" bg="washedGray" borderRadius="1" px="2" my="2">
<Icon stroke="gray" icon="MagnifyingGlass" />
<Input
maxWidth="256px"
@ -198,36 +204,39 @@ export function Participants(props: {
/>
</Row>
<Box
borderBottom={1}
borderBottomColor="washedGray"
gridColumn={["1 / 3", "1 / 4"]}
/>
{filtered.map((cs, idx) => (
<VisibilitySensor
key={idx}
offset={{ top: -800, bottom: -800 }}
partialVisibility
scrollDelay={150}
>
{({ isVisible }) =>
isVisible ? (
cs.map((c) => (
<Participant
api={api}
key={c.patp}
role="admin"
group={props.group}
contact={c}
association={props.association}
/>
))
) : (
<BlankParticipant length={cs.length} />
)
}
</VisibilitySensor>
))}
</Box>
display="grid"
gridAutoRows={["48px 48px 1px", "48px 1px"]}
gridTemplateColumns={["48px 1fr", "48px 2fr 1fr", "48px 3fr 1fr"]}
gridRowGap={2}
alignItems="center"
>
{filtered.map((cs, idx) => (
<VisibilitySensor
key={idx}
offset={{ top: -800, bottom: -800 }}
partialVisibility
scrollDelay={150}
>
{({ isVisible }) =>
isVisible ? (
cs.map((c) => (
<Participant
api={api}
key={c.patp}
role={ourRole}
group={props.group}
contact={c}
association={props.association}
/>
))
) : (
<BlankParticipant length={cs.length} />
)
}
</VisibilitySensor>
))}
</Box>
</Col>
</Col>
);
}
@ -239,7 +248,6 @@ function Participant(props: {
role?: RoleTags;
api: GlobalApi;
}) {
const history = useHistory();
const { contact, association, group, api } = props;
const { title } = association.metadata;
@ -279,19 +287,22 @@ function Participant(props: {
<Sigil ship={contact.patp} size={32} color={`#${color}`} />
</Box>
<Col justifyContent="center" gapY="1" height="100%">
{contact.nickname && <Text>{contact.nickname}</Text>}
<Text color="gray" fontFamily="mono">
{contact.nickname && (
<TruncText title={contact.nickname} maxWidth="85%">
{contact.nickname}
</TruncText>
)}
<Text title={contact.patp} color="gray" fontFamily="mono">
{cite(contact.patp)}
</Text>
</Col>
<Row
width="100%"
justifyContent="space-between"
gridColumn={["1 / 3", "auto"]}
alignItems="center"
>
<Col>
<Text mb={1} color="lightGray">
<Text color="lightGray" mb="1">
Role
</Text>
<Text>{_.capitalize(role)}</Text>
@ -334,7 +345,7 @@ function Participant(props: {
</Col>
}
>
<Icon mr={2} icon="Ellipsis" />
<Icon display="block" icon="Ellipsis" />
</Dropdown>
</Row>
<Box

View File

@ -26,7 +26,7 @@ const SidebarItem = ({ selected, icon, text, to }) => {
py={1}
>
<Icon icon={icon} />
<Text color={selected ? "black" : "gray"}>{text}</Text>
<Text ml="2" color={selected ? "black" : "gray"}>{text}</Text>
</HoverBoxLink>
);
};
@ -57,13 +57,13 @@ export function PopoverRoutes(
const { view } = routeProps.match.params;
return (
<Box
px={[3, 7, 8]}
px={[3, 5, 8]}
py={[3, 5]}
backgroundColor='scales.black30'
left="0px"
top="0px"
width="100vw"
height="100vh"
width="100%"
height="100%"
zIndex={4}
position="fixed"
>
@ -79,7 +79,7 @@ export function PopoverRoutes(
<Box
display="grid"
gridTemplateRows={["32px 1fr", "100%"]}
gridTemplateColumns={["100%", "200px 1fr"]}
gridTemplateColumns={["100%", "150px 1fr"]}
height="100%"
width="100%"
>
@ -96,13 +96,13 @@ export function PopoverRoutes(
text="Participants"
/>
<SidebarItem
icon="Circle"
icon="Gear"
selected={view === "settings"}
to={relativeUrl("/settings")}
text="Group Settings"
/>
<SidebarItem
icon="Circle"
icon="Smiley"
selected={view === "profile"}
to={relativeUrl("/profile")}
text="Group Profile"

View File

@ -39,12 +39,10 @@ export function Resource(props: ResourceProps) {
<Route
path={relativePath("/settings")}
render={(routeProps) => {
const title = `Channel Settings: ${association?.metadata?.title}`;
return (
<ResourceSkeleton
baseUrl={props.baseUrl}
{...skelProps}
title={title}
>
<ChannelSettings api={api} association={association} />
</ResourceSkeleton>

View File

@ -37,7 +37,8 @@ export function ResourceSkeleton(props: ResourceSkeletonProps) {
return (
<Col width="100%" height="100%" overflowY="hidden">
<Box
p={2}
py="2"
px="2"
display="flex"
alignItems="center"
borderBottom={1}
@ -47,8 +48,9 @@ export function ResourceSkeleton(props: ResourceSkeletonProps) {
<Box
borderRight={1}
borderRightColor="gray"
pr={2}
mr={2}
pr={3}
mr={3}
my="1"
display={["block", "none"]}
>
<Link to={`/~landscape${selectedGroup}`}> {"<- Back"}</Link>
@ -56,8 +58,6 @@ export function ResourceSkeleton(props: ResourceSkeletonProps) {
) : (
<Box
color="blue"
borderRight={1}
borderRightColor="gray"
pr={2}
mr={2}
>
@ -66,11 +66,12 @@ export function ResourceSkeleton(props: ResourceSkeletonProps) {
</Link>
</Box>
)}
<Box pr={1} mr={2}>
<Text>{title}</Text>
</Box>
{atRoot && (
<>
<Box pr={1} mr={2}>
<Text>{title}</Text>
</Box>
<TruncatedBox
display={["none", "block"]}
maxWidth="60%"

View File

@ -62,6 +62,7 @@ interface SidebarProps {
// is fixed
const SidebarStickySpacer = styled(Box)`
height: 0px;
flex-grow: 1;
@-moz-document url-prefix() {
& {
height: ${p => p.theme.space[6] }px;
@ -88,12 +89,11 @@ export function Sidebar(props: SidebarProps) {
<Col
display={display}
width="100%"
height="100%"
gridRow="1/2"
gridColumn="1/2"
borderRight={1}
borderRightColor="washedGray"
overflowY="auto"
overflowY="scroll"
fontSize={0}
bg="white"
position="relative"
@ -127,12 +127,14 @@ export function Sidebar(props: SidebarProps) {
/>
<SidebarStickySpacer flexShrink={0} />
<Box
flexShrink="0"
display="flex"
justifyContent="center"
position="sticky"
bottom="8px"
bottom={"8px"}
width="100%"
my={2}
height="fit-content"
py="2"
>
<Link
to={!!groupPath ? `/~landscape${groupPath}/new` : `/~landscape/home/new`}

View File

@ -28,6 +28,7 @@ export function SidebarListHeader(props: {
return (
<Row
flexShrink="0"
alignItems="center"
justifyContent="space-between"
py={2}
@ -70,7 +71,7 @@ export function SidebarListHeader(props: {
</FormikOnBlur>
}
>
<Icon color="gray" icon="Menu" />
<Icon color="gray" icon="Adjust" />
</Dropdown>
</Row>
);

View File

@ -56,8 +56,8 @@ export function Skeleton(props: SkeletonProps) {
return (
<Body
display="grid"
gridTemplateColumns={["1fr", "250px 1fr"]}
gridTemplateRows="1fr"
gridTemplateColumns={["100%", "250px 1fr"]}
gridTemplateRows="100%"
>
{!props.hideSidebar && (
<Sidebar