Merge branch 'release/next-js' into release/next-userspace

This commit is contained in:
Matilde Park 2021-01-22 17:01:28 -05:00
commit d3c52c5e19
23 changed files with 268 additions and 136 deletions

View File

@ -62,7 +62,7 @@ jobs:
- uses: cachix/cachix-action@v8
with:
name: ${{ secrets.CACHIX_NAME }}
name: ares
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
- run: nix-build -A urbit --arg enableStatic true
@ -88,7 +88,7 @@ jobs:
- uses: cachix/install-nix-action@v12
- uses: cachix/cachix-action@v8
with:
name: ${{ secrets.CACHIX_NAME }}
name: ares
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
- run: nix-build -A hs.urbit-king.components.exes.urbit-king --arg enableStatic true

View File

@ -22,7 +22,7 @@ jobs:
system-features = nixos-test benchmark big-parallel kvm
- uses: cachix/cachix-action@v8
with:
name: ${{ secrets.CACHIX_NAME }}
name: ares
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
- uses: docker/docker-login-action@v1.8.0
with:

View File

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:288b3ab68e2e3946dbf0e0de05c2907c351ec97fcc08134e558569eab4121c94
size 8809816
oid sha256:271d575a87373f4ed73b195780973ed41cb72be21b428a645c42a49ab5f786ee
size 8873583

View File

@ -5,7 +5,7 @@
/- glob
/+ default-agent, verb, dbug
|%
++ hash 0v1.4u9gp.rs1fi.ki7ok.ib4cp.mgdvs
++ hash 0v7.ttn7o.50403.rf6oh.63hnc.hgpc9
+$ state-0 [%0 hash=@uv glob=(unit (each glob:glob tid=@ta))]
+$ all-states
$% state-0

View File

@ -24,6 +24,6 @@
<div id="portal-root"></div>
<script src="/~landscape/js/channel.js"></script>
<script src="/~landscape/js/session.js"></script>
<script src="/~landscape/js/bundle/index.86c6e416c338a305e1e9.js"></script>
<script src="/~landscape/js/bundle/index.7d4248944fe1255cb74b.js"></script>
</body>
</html>

View File

@ -180,6 +180,9 @@
$(index +(index), sorted [(~(got by fragments) index) sorted])
::
(cue (rep 13 (flop sorted)))
:: +jim: caching +jam
::
++ jim |=(n=* ~+((jam n)))
:: +bind-duct: find or make new $bone for .duct in .ossuary
::
++ bind-duct
@ -1931,7 +1934,7 @@
==
now
::
=/ =message-blob (dedup-message (jam payload))
=/ =message-blob (dedup-message (jim payload))
=. peer-core (run-message-pump bone %memo message-blob)
::
?: &(=(%boon valence) ?=(?(%dead %unborn) -.qos.peer-state))

View File

@ -29,13 +29,16 @@
^- form:m
=/ pax
(en-path:resource rid)
=/ hold=@dr ~s0..8000
|- ^- form:m
?> (lte hold ~m5)
=* loop $
;< u-group=(unit group) bind:m
(scry:strandio ,(unit group) (weld /gx/group-store/groups (snoc pax %noun)))
?^ u-group
(pure:m ~)
;< ~ bind:m (sleep:strandio `@dr`(div ~s1 2))
;< ~ bind:m (sleep:strandio hold)
=. hold (mul hold 2)
loop
::
++ wait-for-md
@ -44,13 +47,16 @@
^- form:m
=/ pax
(en-path:resource rid)
=/ hold=@dr ~s0..8000
|- ^- form:m
?> (lte hold ~m5)
=* loop $
;< groups=(set path) bind:m
(scry:strandio ,(set path) /gy/metadata-store/group-indices)
?: (~(has in groups) pax)
;< groups=(jug path md-resource) bind:m
(scry:strandio ,(jug path md-resource) /gy/metadata-store/group-indices)
?: (~(has by groups) pax)
(pure:m ~)
;< ~ bind:m (sleep:strandio `@dr`(div ~s1 2))
;< ~ bind:m (sleep:strandio hold)
=. hold (mul hold 2)
loop
--
::

Binary file not shown.

View File

@ -4,85 +4,85 @@
"description": "",
"main": "index.js",
"dependencies": {
"@babel/runtime": "^7.10.5",
"@babel/runtime": "^7.12.5",
"@reach/disclosure": "^0.10.5",
"@reach/menu-button": "^0.10.5",
"@reach/tabs": "^0.10.5",
"@tlon/indigo-dark": "^1.0.6",
"@tlon/indigo-light": "^1.0.6",
"@tlon/indigo-react": "1.2.16",
"@tlon/indigo-react": "1.2.17",
"@tlon/sigil-js": "^1.4.3",
"aws-sdk": "^2.726.0",
"aws-sdk": "^2.830.0",
"big-integer": "^1.6.48",
"classnames": "^2.2.6",
"codemirror": "^5.55.0",
"css-loader": "^3.5.3",
"file-saver": "^2.0.2",
"formik": "^2.1.4",
"immer": "^8.0.0",
"lodash": "^4.17.15",
"codemirror": "^5.59.2",
"css-loader": "^3.6.0",
"file-saver": "^2.0.5",
"formik": "^2.2.6",
"immer": "^8.0.1",
"lodash": "^4.17.20",
"markdown-to-jsx": "^6.11.4",
"moment": "^2.20.1",
"moment": "^2.29.1",
"mousetrap": "^1.6.5",
"mousetrap-global-bind": "^1.1.0",
"normalize-wheel": "1.0.1",
"oembed-parser": "^1.4.1",
"oembed-parser": "^1.4.5",
"prop-types": "^15.7.2",
"react": "^16.5.2",
"react": "^16.14.0",
"react-codemirror2": "^6.0.1",
"react-dom": "^16.8.6",
"react-dom": "^16.14.0",
"react-helmet": "^6.1.0",
"react-markdown": "^4.3.1",
"react-oembed-container": "^1.0.0",
"react-router-dom": "^5.0.0",
"react-virtuoso": "^0.20.0",
"react-router-dom": "^5.2.0",
"react-virtuoso": "^0.20.3",
"react-visibility-sensor": "^5.1.1",
"remark-breaks": "^2.0.1",
"remark-disable-tokenizers": "^1.0.24",
"style-loader": "^1.2.1",
"styled-components": "^5.1.0",
"style-loader": "^1.3.0",
"styled-components": "^5.1.1",
"styled-system": "^5.1.5",
"suncalc": "^1.8.0",
"urbit-ob": "^5.0.0",
"urbit-ob": "^5.0.1",
"yup": "^0.29.3",
"zustand": "^3.2.0"
"zustand": "^3.3.1"
},
"devDependencies": {
"@babel/core": "^7.9.0",
"@babel/plugin-proposal-class-properties": "^7.8.3",
"@babel/plugin-proposal-object-rest-spread": "^7.9.5",
"@babel/plugin-proposal-optional-chaining": "^7.9.0",
"@babel/plugin-transform-runtime": "^7.10.5",
"@babel/preset-env": "^7.9.5",
"@babel/preset-react": "^7.9.4",
"@babel/preset-typescript": "^7.10.1",
"@types/lodash": "^4.14.155",
"@types/react": "^16.9.38",
"@types/react-dom": "^16.9.8",
"@types/react-router-dom": "^5.1.5",
"@types/styled-components": "^5.1.2",
"@babel/core": "^7.12.10",
"@babel/plugin-proposal-class-properties": "^7.12.1",
"@babel/plugin-proposal-object-rest-spread": "^7.12.1",
"@babel/plugin-proposal-optional-chaining": "^7.12.7",
"@babel/plugin-transform-runtime": "^7.12.10",
"@babel/preset-env": "^7.12.11",
"@babel/preset-react": "^7.12.10",
"@babel/preset-typescript": "^7.12.7",
"@types/lodash": "^4.14.168",
"@types/react": "^16.14.2",
"@types/react-dom": "^16.9.10",
"@types/react-router-dom": "^5.1.7",
"@types/styled-components": "^5.1.7",
"@types/styled-system": "^5.1.10",
"@types/yup": "^0.29.7",
"@typescript-eslint/eslint-plugin": "^3.8.0",
"@typescript-eslint/parser": "^3.8.0",
"@types/yup": "^0.29.11",
"@typescript-eslint/eslint-plugin": "^3.10.1",
"@typescript-eslint/parser": "^3.10.1",
"babel-eslint": "^10.1.0",
"babel-loader": "^8.1.0",
"babel-loader": "^8.2.2",
"babel-plugin-lodash": "^3.3.4",
"babel-plugin-root-import": "^6.5.0",
"babel-plugin-root-import": "^6.6.0",
"clean-webpack-plugin": "^3.0.0",
"cross-env": "^7.0.2",
"cross-env": "^7.0.3",
"eslint": "^6.8.0",
"eslint-plugin-react": "^7.19.0",
"file-loader": "^6.0.0",
"html-webpack-plugin": "^4.2.0",
"eslint-plugin-react": "^7.22.0",
"file-loader": "^6.2.0",
"html-webpack-plugin": "^4.5.1",
"moment-locales-webpack-plugin": "^1.2.0",
"react-hot-loader": "^4.12.21",
"sass": "^1.26.5",
"react-hot-loader": "^4.13.0",
"sass": "^1.32.5",
"sass-loader": "^8.0.2",
"typescript": "^3.9.7",
"webpack": "^4.43.0",
"webpack-cli": "^3.3.11",
"webpack-dev-server": "^3.10.3"
"webpack": "^4.46.0",
"webpack-cli": "^3.3.12",
"webpack-dev-server": "^3.11.2"
},
"scripts": {
"lint": "eslint ./src/**/*.{js,ts,tsx}",

View File

@ -3,8 +3,8 @@ import { StoreState } from '../store/type';
import { Patp, Path, PatpNoSig } from '~/types/noun';
import _ from 'lodash';
import {makeResource, resourceFromPath} from '../lib/group';
import {GroupPolicy, Enc, Post, NodeMap, Content} from '~/types';
import { numToUd, unixToDa, decToUd, deSig } from '~/logic/lib/util';
import {GroupPolicy, Enc, Post, NodeMap, Content, Resource} from '~/types';
import { numToUd, unixToDa, decToUd, deSig, resourceAsPath } from '~/logic/lib/util';
export const createBlankNodeWithChildPost = (
parentIndex: string = '',
@ -81,6 +81,8 @@ function moduleToMark(mod: string): string | undefined {
export default class GraphApi extends BaseApi<StoreState> {
joiningGraphs = new Set<string>();
private storeAction(action: any): Promise<any> {
return this.action('graph-store', 'graph-update', action)
}
@ -138,11 +140,19 @@ export default class GraphApi extends BaseApi<StoreState> {
joinGraph(ship: Patp, name: string) {
const resource = makeResource(ship, name);
const rid = resourceAsPath(resource);
if(this.joiningGraphs.has(rid)) {
return Promise.resolve();
}
this.joiningGraphs.add(rid);
return this.viewAction('graph-join', {
join: {
resource,
ship,
}
}).then(res => {
this.joiningGraphs.delete(rid);
return res;
});
}

View File

@ -282,7 +282,7 @@ export const MessageContent = ({ content, contacts, measure, fontSize, group })
url={content.url}
onLoad={measure}
imageProps={{style: {
maxWidth: '18rem',
maxWidth: 'min(100%,18rem)',
display: 'block'
}}}
videoProps={{style: {

View File

@ -1,4 +1,4 @@
import React, { useEffect } from "react";
import React, { useEffect, useCallback } from "react";
import { Box, Row, Col, Center, LoadingSpinner, Text } from "@tlon/indigo-react";
import { Switch, Route, Link } from "react-router-dom";
import bigInt from 'big-integer';
@ -10,10 +10,14 @@ import { RouteComponentProps } from "react-router-dom";
import { LinkItem } from "./components/LinkItem";
import LinkSubmit from "./components/LinkSubmit";
import { LinkPreview } from "./components/link-preview";
import { LinkWindow } from "./LinkWindow";
import { Comments } from "~/views/components/Comments";
import "./css/custom.css";
const emptyMeasure = () => {};
type LinkResourceProps = StoreState & {
association: Association;
api: GlobalApi;
@ -57,39 +61,28 @@ export function LinkResource(props: LinkResourceProps) {
return <Center width='100%' height='100%'><LoadingSpinner/></Center>;
}
return (
<Col alignItems="center" height="100%" width="100%" overflowY="auto">
<Col alignItems="center" height="100%" width="100%" overflowY="hidden">
<Switch>
<Route
exact
path={relativePath("")}
render={(props) => {
return (
<Col width="100%" p={4} alignItems="center" maxWidth="768px">
<Col width="100%" flexShrink='0'>
<LinkSubmit s3={s3} name={name} ship={ship.slice(1)} api={api} />
</Col>
{Array.from(graph).map(([date, node]) => {
const contact = contactDetails[node.post.author];
return (
<LinkItem
association={resource}
contacts={contacts}
key={date.toString()}
resource={resourcePath}
node={node}
contacts={contactDetails}
unreads={unreads}
nickname={contact?.nickname}
baseUrl={resourceUrl}
group={group}
path={resource["group-path"]}
api={api}
mb={3}
/>
);
})}
</Col>
<LinkWindow
s3={s3}
association={resource}
contacts={contacts}
resource={resourcePath}
graph={graph}
unreads={unreads}
baseUrl={resourceUrl}
group={group}
path={resource["group-path"]}
api={api}
mb={3}
/>
);
}}
/>
@ -112,6 +105,7 @@ export function LinkResource(props: LinkResourceProps) {
const contact = contactDetails[node.post.author];
return (
<Col alignItems="center" overflowY="auto" width="100%">
<Col width="100%" p={3} maxWidth="768px">
<Link to={resourceUrl}><Text bold>{"<- Back"}</Text></Link>
<LinkItem
@ -125,6 +119,7 @@ export function LinkResource(props: LinkResourceProps) {
path={resource["group-path"]}
api={api}
mt={3}
measure={emptyMeasure}
/>
<Comments
ship={ship}
@ -141,6 +136,7 @@ export function LinkResource(props: LinkResourceProps) {
group={group}
/>
</Col>
</Col>
);
}}
/>

View File

@ -0,0 +1,97 @@
import React, { useRef, useCallback, useEffect, useMemo } from "react";
import { Col } from "@tlon/indigo-react";
import bigInt from 'big-integer';
import {
Association,
Graph,
Contacts,
Unreads,
LocalUpdateRemoteContentPolicy,
Group,
Rolodex,
S3State,
} from "~/types";
import GlobalApi from "~/logic/api/global";
import VirtualScroller from "~/views/components/VirtualScroller";
import { LinkItem } from "./components/LinkItem";
import LinkSubmit from "./components/LinkSubmit";
interface LinkWindowProps {
association: Association;
contacts: Rolodex;
resource: string;
graph: Graph;
unreads: Unreads;
hideNicknames: boolean;
hideAvatars: boolean;
baseUrl: string;
group: Group;
path: string;
api: GlobalApi;
s3: S3State;
}
export function LinkWindow(props: LinkWindowProps) {
const { graph, api, association } = props;
const loadedNewest = useRef(true);
const loadedOldest = useRef(false);
const virtualList = useRef<VirtualScroller>();
const fetchLinks = useCallback(
async (newer: boolean) => {
/* stubbed, should we generalize the display of graphs in virtualscroller? */
}, []
);
useEffect(() => {
const list = virtualList?.current;
if(!list) return;
list.calculateVisibleItems();
}, [graph.size]);
const first = graph.peekLargest()?.[0];
const [,,ship, name] = association['app-path'].split('/');
const style = useMemo(() =>
({
height: "100%",
width: "100%",
display: 'flex',
flexDirection: 'column',
alignItems: 'center'
}), []);
return (
<VirtualScroller
ref={(l) => (virtualList.current = l ?? undefined)}
origin="top"
style={style}
onStartReached={() => {}}
onScroll={() => {}}
data={graph}
size={graph.size}
renderer={({ index, measure, scrollWindow }) => {
const node = graph.get(index);
const post = node?.post;
if (!node || !post) return null;
const linkProps = {
...props,
node,
measure,
key: index.toString()
};
if(index.eq(first ?? bigInt.zero)) {
return (
<>
<Col key={index.toString()} mx="auto" mt="4" maxWidth="768px" width="100%" flexShrink='0' px={3}>
<LinkSubmit s3={props.s3} name={name} ship={ship.slice(1)} api={api} />
</Col>
<LinkItem {...linkProps} />
</>
)
}
return <LinkItem {...linkProps} />;
}}
loadRows={fetchLinks}
/>
);
}

View File

@ -1,4 +1,4 @@
import React, { useState } from 'react';
import React, { useState, useEffect, useRef, useCallback } from 'react';
import { Link } from 'react-router-dom';
import { Row, Col, Anchor, Box, Text, Icon, Action } from '@tlon/indigo-react';
@ -17,8 +17,9 @@ interface LinkItemProps {
api: GlobalApi;
group: Group;
path: string;
contacts: Rolodex[];
contacts: Rolodex;
unreads: Unreads;
measure: (el: any) => void;
}
export const LinkItem = (props: LinkItemProps) => {
@ -29,9 +30,12 @@ export const LinkItem = (props: LinkItemProps) => {
group,
path,
contacts,
measure,
...rest
} = props;
const ref = useRef<HTMLDivElement | null>(null);
const URLparser = new RegExp(
/((?:([\w\d\.-]+)\:\/\/?){1}(?:(www)\.?){0,1}(((?:[\w\d-]+\.)*)([\w\d-]+\.[\w\d]+))){1}(?:\:(\d+)){0,1}((\/(?:(?:[^\/\s\?]+\/)*))(?:([^\?\/\s#]+?(?:.[^\?\s]+){0,1}){0,1}(?:\?([^\s#]+)){0,1})){0,1}(?:#([^#\s]+)){0,1}/
);
@ -70,9 +74,18 @@ export const LinkItem = (props: LinkItemProps) => {
const markRead = () => {
api.hark.markEachAsRead(props.association, '/', `/${index}`, 'link', 'link');
}
return (
<Box width="100%" {...rest}>
const onMeasure = useCallback(() => {
ref.current && measure(ref.current);
}, [ref.current, measure])
useEffect(() => {
onMeasure();
}, [onMeasure]);
return (
<Box mx="auto" px={3} maxWidth="768px" ref={ref} width="100%" {...rest}>
<Box
lineHeight="tall"
display='flex'
@ -90,6 +103,7 @@ export const LinkItem = (props: LinkItemProps) => {
url={contents[1].url}
text={contents[0].text}
unfold={true}
onLoad={onMeasure}
style={{ alignSelf: 'center' }}
oembedProps={{
p: 2,

View File

@ -146,7 +146,7 @@ export default function Inbox(props: {
return (
<Col position="relative" height="100%" overflowY="auto" onScroll={onScroll} >
<Col zIndex={4} gapY={2} bg="white" top="0px" position="sticky">
<Col zIndex={4} gapY={2} bg="white" top="0px" position="sticky" flexShrink={0}>
{inviteItems(invites, api)}
</Col>
{[...notificationsByDay.keys()].map((day, index) => {

View File

@ -264,7 +264,7 @@
background-color: #333;
}
.publish .cm-s-tlon.CodeMirror {
background: #333;
background: unset;
color: #fff;
}

View File

@ -29,10 +29,15 @@ export function AsyncButton({
}, [status]);
return (
<Button disabled={!isValid} type="submit" {...rest}>
<Button
hideDisabled={isSubmitting}
disabled={!isValid || isSubmitting}
type="submit"
{...rest}
>
{isSubmitting ? (
<LoadingSpinner
foreground={rest.primary ? "white" : 'black'}
foreground={rest.primary ? "white" : "black"}
background="gray"
/>
) : success === true ? (

View File

@ -8,6 +8,7 @@ import { useFormikContext } from "formik";
interface AsyncActionProps {
children: ReactNode;
name: string;
disabled?: boolean;
onClick: (e: React.MouseEvent) => Promise<void>;
}
@ -15,6 +16,7 @@ export function StatelessAsyncAction({
children,
onClick,
name = '',
disabled = false,
...rest
}: AsyncActionProps & Parameters<typeof Action>[0]) {
const {
@ -23,7 +25,10 @@ export function StatelessAsyncAction({
} = useStatelessAsyncClickable(onClick, name);
return (
<Action onClick={handleClick} {...rest}>
<Action
hideDisabled={!disabled}
disabled={disabled || state === 'loading'}
onClick={handleClick} {...rest}>
{state === "error" ? (
"Error"
) : state === "loading" ? (

View File

@ -7,7 +7,7 @@ import { useStatelessAsyncClickable } from "~/logic/lib/useStatelessAsyncClickab
interface AsyncButtonProps {
children: ReactNode;
name: string;
name?: string;
onClick: (e: React.MouseEvent) => Promise<void>;
}
@ -15,6 +15,7 @@ export function StatelessAsyncButton({
children,
onClick,
name = "",
disabled = false,
...rest
}: AsyncButtonProps & Parameters<typeof Button>[0]) {
const {
@ -23,7 +24,12 @@ export function StatelessAsyncButton({
} = useStatelessAsyncClickable(onClick, name);
return (
<Button onClick={handleClick} {...rest}>
<Button
hideDisabled={!disabled}
disabled={disabled || state === 'loading'}
onClick={handleClick}
{...rest}
>
{state === "error" ? (
"Error"
) : state === "loading" ? (

View File

@ -44,6 +44,8 @@ export default class VirtualScroller extends Component<VirtualScrollerProps, Vir
timeout: ReturnType<typeof setTimeout>;
} | undefined;
overscan = 150;
OVERSCAN_SIZE = 100; // Minimum number of messages on either side before loadRows is called
constructor(props: VirtualScrollerProps) {
@ -53,7 +55,7 @@ export default class VirtualScroller extends Component<VirtualScrollerProps, Vir
visibleItems: new BigIntOrderedMap(),
endgap: props.origin === 'bottom' ? 0 : undefined,
totalHeight: 0,
averageHeight: 64,
averageHeight: 130,
scrollTop: props.origin === 'top' ? 0 : undefined
};
@ -61,8 +63,8 @@ export default class VirtualScroller extends Component<VirtualScrollerProps, Vir
this.window = null;
this.cache = new BigIntOrderedMap();
this.recalculateTotalHeight = this.recalculateTotalHeight.bind(this);
this.calculateVisibleItems = this.calculateVisibleItems.bind(this);
this.recalculateTotalHeight = _.throttle(this.recalculateTotalHeight.bind(this), 200);
this.calculateVisibleItems = _.throttle(this.calculateVisibleItems.bind(this), 200);
this.estimateIndexFromScrollTop = this.estimateIndexFromScrollTop.bind(this);
this.invertedKeyHandler = this.invertedKeyHandler.bind(this);
this.heightOf = this.heightOf.bind(this);
@ -74,6 +76,8 @@ export default class VirtualScroller extends Component<VirtualScrollerProps, Vir
componentDidMount() {
this.calculateVisibleItems();
this.recalculateTotalHeight();
}
componentDidUpdate(prevProps: VirtualScrollerProps, prevState: VirtualScrollerState) {
@ -107,7 +111,7 @@ export default class VirtualScroller extends Component<VirtualScrollerProps, Vir
let { averageHeight } = this.state;
let totalHeight = 0;
this.props.data.forEach((datum, index) => {
totalHeight += this.heightOf(index);
totalHeight += Math.max(this.heightOf(index), 0);
});
averageHeight = Number((totalHeight / this.props.data.size).toFixed());
totalHeight += (this.props.size - this.props.data.size) * averageHeight;
@ -136,41 +140,23 @@ export default class VirtualScroller extends Component<VirtualScrollerProps, Vir
let startgap = 0, heightShown = 0, endgap = 0;
let startGapFilled = false;
let visibleItems = new BigIntOrderedMap<any>();
let startBuffer = new BigIntOrderedMap<any>();
let endBuffer = new BigIntOrderedMap<any>();
const { scrollTop, offsetHeight: windowHeight } = this.window;
const { averageHeight } = this.state;
const { averageHeight, totalHeight } = this.state;
const { data, size: totalSize, onCalculateVisibleItems } = this.props;
console.log(windowHeight);
const overscan = Math.max(windowHeight / 2, 200);
[...data].forEach(([index, datum]) => {
const height = this.heightOf(index);
if (startgap < (scrollTop - overscan) && !startGapFilled) {
startBuffer.set(index, datum);
if (startgap < (scrollTop - this.overscan) && !startGapFilled) {
startgap += height;
} else if (heightShown < (windowHeight + overscan)) {
} else if (heightShown < (windowHeight + this.overscan)) {
startGapFilled = true;
visibleItems.set(index, datum);
heightShown += height;
} else if (endBuffer.size < visibleItems.size) {
endBuffer.set(index, data.get(index));
} else {
endgap += height;
}
});
startBuffer = new BigIntOrderedMap(
[...startBuffer].reverse().slice(0, (visibleItems.size - visibleItems.size % 5))
);
startBuffer.forEach((_datum, index) => {
startgap -= this.heightOf(index);
}
});
endgap = totalHeight - heightShown - startgap;
const firstVisibleKey = visibleItems.peekSmallest()?.[0] ?? this.estimateIndexFromScrollTop(scrollTop)!;
const smallest = data.peekSmallest();
@ -189,7 +175,7 @@ export default class VirtualScroller extends Component<VirtualScrollerProps, Vir
onCalculateVisibleItems ? onCalculateVisibleItems(visibleItems) : null;
this.setState({
startgap: Number(startgap.toFixed()),
visibleItems: new BigIntOrderedMap([...startBuffer, ...visibleItems, ...endBuffer]),
visibleItems,
endgap: Number(endgap.toFixed()),
});
}
@ -238,6 +224,8 @@ export default class VirtualScroller extends Component<VirtualScrollerProps, Vir
}
}
this.overscan = Math.max(element.offsetHeight * 3, 500);
this.window = element;
if (this.props.origin === 'bottom') {
element.addEventListener('wheel', (event) => {
@ -303,7 +291,7 @@ export default class VirtualScroller extends Component<VirtualScrollerProps, Vir
data
} = this.props;
const indexesToRender = visibleItems.keys().reverse();
const indexesToRender = origin === 'top' ? visibleItems.keys() : visibleItems.keys().reverse();
const transform = origin === 'top' ? 'scale3d(1, 1, 1)' : 'scale3d(1, -1, 1)';
@ -314,7 +302,7 @@ export default class VirtualScroller extends Component<VirtualScrollerProps, Vir
height: element.offsetHeight,
element
});
_.debounce(this.recalculateTotalHeight, 500)();
this.recalculateTotalHeight();
}
};
return renderer({ index, measure, scrollWindow: this.window });
@ -322,7 +310,7 @@ export default class VirtualScroller extends Component<VirtualScrollerProps, Vir
return (
<Box overflowY='scroll' ref={this.setWindow.bind(this)} onScroll={this.onScroll.bind(this)} style={{ ...style, ...{ transform } }}>
<Box ref={this.scrollContainer} style={{ transform }}>
<Box ref={this.scrollContainer} style={{ transform, width: '100%' }}>
<Box style={{ height: `${origin === 'top' ? startgap : endgap}px` }}></Box>
{indexesToRender.map(render)}
<Box style={{ height: `${origin === 'top' ? endgap : startgap}px` }}></Box>

View File

@ -102,7 +102,7 @@ export function ChannelMenu(props: ChannelMenuProps) {
</ChannelMenuItem>
<ChannelMenuItem bottom icon="Gear" color="black">
<Link to={`${baseUrl}/settings`}>
<Box fontSize={0} p="2">
<Box fontSize={1} p="2">
Channel Settings
</Box>
</Link>

View File

@ -72,6 +72,7 @@ export function ResourceSkeleton(props: ResourceSkeletonProps) {
mr={3}
my="1"
display={["block", "none"]}
flexShrink={0}
>
<Link to={`/~landscape${workspace}`}> {"<- Back"}</Link>
</Box>
@ -85,8 +86,8 @@ export function ResourceSkeleton(props: ResourceSkeletonProps) {
{atRoot && (
<>
<Box pr={1} mr={2}>
<Text fontSize='2' fontWeight='700' display="inline-block" verticalAlign="middle" textOverflow="ellipsis" overflow="hidden" whiteSpace="pre">
<Box px={1} mr={2} minWidth={0} display="flex">
<Text fontSize='2' fontWeight='700' display="inline-block" verticalAlign="middle" textOverflow="ellipsis" overflow="hidden" whiteSpace="pre" minWidth={0}>
{title}
</Text>
</Box>

View File

@ -58,13 +58,14 @@ export function SidebarListHeader(props: {
<Box
textAlign='right'
display='flex'
alignItems='center'
>
<Link
style={{
display: isAdmin ? "inline-block" : "none" }}
to={
!!groupPath ? `/~landscape${groupPath}/new` : `/~landscape/home/new`}>
<Icon icon="Plus" color="gray" pr='2'/>
<Icon icon="Plus" color="gray" pr='12px'/>
</Link>
<Link to={`${props.baseUrl}/invites`}
style={{ display: (props.workspace?.type === 'home') ? 'inline-block' : 'none'}}>
@ -72,7 +73,7 @@ export function SidebarListHeader(props: {
display='inline-block'
py='1px'
px='3px'
mr='2'
mr='12px'
backgroundColor='washedBlue'
color='blue'
borderRadius='1'>