launch: fixing layout

This commit is contained in:
Tyler Brown Cifu Shuster 2020-10-06 11:57:51 -07:00
parent 34235d53f2
commit 7e57ec2f96
7 changed files with 224 additions and 264 deletions

View File

@ -9,29 +9,10 @@ import './css/custom.css';
import { Sigil } from "~/logic/lib/sigil";
import Tiles from './components/tiles';
import Tile from './components/tiles/tile';
import Welcome from './components/welcome';
import Groups from './components/Groups';
const Tile = ({ children, bg, to, ...rest }) => (
<Box
mt='0'
ml='2'
mr='2'
mb={3}
bg="white"
width="126px"
height="126px"
borderRadius={2}
overflow="hidden"
{...rest}>
<Link to={to}>
<Box p={2} bg={bg} width="100%" height="100%">
{children}
</Box>
</Link>
</Box>
);
export default class LaunchApp extends React.Component {
componentDidMount() {
@ -57,7 +38,14 @@ export default class LaunchApp extends React.Component {
</Helmet>
<div className="h-100 overflow-y-scroll">
<Welcome firstTime={props.launch.firstTime} api={props.api} />
<Row ml='2' flexWrap="wrap" mb={4} pitch={4}>
<Box
ml='2'
display='grid'
gridAutoRows='124px'
gridTemplateColumns='repeat(auto-fit, 124px)'
gridGap={3}
p={2}
>
<Tile
border={1}
bg="washedGreen"
@ -89,7 +77,7 @@ export default class LaunchApp extends React.Component {
location={props.userLocation}
weather={props.weather}
/>
</Row>
</Box>
<Groups associations={props.associations} />
<Box
position="absolute"

View File

@ -1,5 +1,5 @@
import React from "react";
import { Box } from "@tlon/indigo-react";
import { Box, Text } from "@tlon/indigo-react";
import { Link } from "react-router-dom";
import { useLocalStorageState } from "~/logic/lib/useLocalStorageState";
@ -62,12 +62,12 @@ export default function Groups(props: GroupsProps & Parameters<typeof Box>[0]) {
width="100%"
bg="white"
border={1}
borderRadius={1}
borderRadius={2}
borderColor="lightGray"
p={2}
fontSize={0}
>
{group.metadata.title}
<Text>{group.metadata.title}</Text>
</Box>
</Link>
))}

View File

@ -1,7 +1,6 @@
import React from 'react';
import classnames from 'classnames';
import { Link } from 'react-router-dom';
import defaultApps from '~/logic/lib/default-apps';
import { Text, Icon } from '@tlon/indigo-react';
import Tile from './tile';
@ -9,47 +8,22 @@ export default class BasicTile extends React.PureComponent {
render() {
const { props } = this;
const children = (
<span>
<p className={
classnames('absolute f9',
{ 'black white-d': props.title !== 'Dojo',
'white': props.title === 'Dojo' })}
style={{ left: 8, top: 8 }}
>
{props.title}
</p>
<img
className={classnames('absolute',
{ 'invert-d': props.title !== 'Dojo' })}
style={{ left: 38, top: 38 }}
src={props.iconUrl}
width={48}
height={48}
/>
</span>
);
const routeList = defaultApps.map((e) => {
return `/~${e}`;
});
const tile = ( routeList.indexOf(props.linkedUrl) !== -1 ) ? (
<Link className="w-100 h-100 db pa2 no-underline" to={props.linkedUrl}>
{children}
</Link>
) : (
<a className="w-100 h-100 db pa2 no-underline" href={props.linkedUrl}>
{children}
</a>
);
return (
<Tile>
<div className={classnames('w-100 h-100 relative ba b--gray3 b--gray2-d bg-gray0-d br2',
{ 'bg-white': props.title !== 'Dojo',
'bg-black': props.title === 'Dojo' })}
>{tile}</div>
<Tile
bg={props.title === 'Dojo' ? '#000000' : 'white'}
to={props.linkedUrl}
>
<Text color={props.title === 'Dojo' ? '#ffffff' : 'black'}>
{props.title === 'Dojo'
? <Icon
icon='ChevronEast'
color='transparent'
color='#fff'
style={{ position: 'relative', top: '.3em'}}
/>
: null
}{props.title}
</Text>
</Tile>
);
}

View File

@ -63,13 +63,21 @@ const circle = (ctx, x, y, r, from, to, fill) => {
ctx.fill();
};
const circleClip = (ctx, x, y, r, from, to, fill) => {
ctx.globalCompositeOperation = 'xor';
circle(ctx, x, y, r, from, to, fill);
ctx.globalCompositeOperation = 'source-over';
};
const circleOutline = (ctx, x, y, r, from, to, stroke, lineWidth) => {
ctx.beginPath();
ctx.arc( x, y, r, from, to );
ctx.fillStyle = 'rgba(0,0,0,0)';
ctx.lineWidth = lineWidth;
ctx.strokeStyle = stroke || 'rgba(0,0,0,0)';
ctx.stroke();
if (lineWidth) {
ctx.stroke();
}
};
const arc = (ctx, x, y, r, from, to, fill) => {
@ -211,7 +219,7 @@ class Clock extends React.Component {
ctr / 2,
state.sunriseEnd,
state.sunset,
'#6792FF'
'rgba(33, 157, 255, .3)'
);
// Sunrise
@ -220,9 +228,9 @@ class Clock extends React.Component {
ctr,
ctr,
ctr / 2,
state.nightEnd,
state.sunsetStart,
state.sunriseEnd,
'#FCC440'
'#FFC700'
);
// Sunset
@ -231,31 +239,9 @@ class Clock extends React.Component {
ctr,
ctr,
ctr / 2,
state.nightEnd,
splitArc(state.sunriseEnd, state.nightEnd),
'#FF611E'
);
// Sunset
degArc(
ctx,
ctr,
ctr,
ctr / 2,
state.sunset,
state.night,
'#FCC440'
);
// Sunset
degArc(
ctx,
ctr,
ctr,
ctr / 2,
splitArc(state.sunset, state.night),
state.night,
'#FF611E'
state.dusk,
state.dawn,
'rgba(255, 65, 54, .8)'
);
// Night
@ -266,7 +252,7 @@ class Clock extends React.Component {
ctr / 2,
state.night,
state.nightEnd,
'rgb(26, 26, 26)'
'rgba(0, 0, 0, .8)'
);
if (
@ -328,20 +314,8 @@ class Clock extends React.Component {
ctr-1,
-1,
2 * Math.PI,
text,
1
);
// Outer borders
circle(
ctx,
ctr,
ctr,
ctr/1.85,
-1,
2 * Math.PI,
background,
1
'none',
0
);
// Center white circle border
@ -352,8 +326,19 @@ class Clock extends React.Component {
ctr/1.85,
-1,
2 * Math.PI,
text,
1
'none',
0
);
// Inner hole
circle(
ctx,
ctr,
ctr,
ctr/1.85,
-1,
2 * Math.PI,
background
);
// Text for time and date
@ -373,13 +358,13 @@ class Clock extends React.Component {
}
render() {
return <div style={{ position:'relative' }}>
return (
<canvas
style={{ position:'absolute' }}
ref={ canvasRef => this.canvasRef = canvasRef }
id="clock-canvas"
style={{ height: '100%', width: '100%'}}
ref={ canvasRef => this.canvasRef = canvasRef }
id="clock-canvas"
/>
</div>;
);
}
}
@ -390,7 +375,7 @@ export default class ClockTile extends React.Component {
renderWrapper(child) {
return (
<Tile transparent>
<Tile p={0} bg="transparent" border={0}>
{child}
</Tile>
);

View File

@ -1,15 +1,41 @@
import React from 'react';
import { Link } from 'react-router-dom';
import defaultApps from '~/logic/lib/default-apps';
const routeList = defaultApps.map((e) => {
return `/~${e}`;
});
import { Box } from "@tlon/indigo-react";
export default class Tile extends React.Component {
render() {
const { transparent } = this.props;
const bgClasses = transparent ? ' ' : ' bg-transparent ';
const { bg, to, p, ...props } = this.props;
let childElement = (
<Box p={typeof p === 'undefined' ? 2 : p} bg={bg} width="100%" height="100%">
{props.children}
</Box>
);
if (to) {
if (routeList.indexOf(to) === -1) {
childElement= (<Link to={to}>{childElement}</Link>);
} else {
childElement= (<a href={to}>{childElement}</a>);
}
}
return (
<div className={"fl mr2 ml2 mb3 mt0 overflow-hidden" + bgClasses}
style={{ height: '126px', width: '126px' }}>
{this.props.children}
</div>
<Box
border={1}
borderRadius={2}
borderColor="lightGray"
overflow="hidden"
{...props}
>
{childElement}
</Box>
);
}
}

View File

@ -1,5 +1,6 @@
import React from 'react';
import moment from 'moment';
import { Box, Icon, Text } from '@tlon/indigo-react';
import Tile from './tile';
@ -47,87 +48,53 @@ export default class WeatherTile extends React.Component {
// set appearance based on weather
setColors(data) {
let weatherStyle = {
gradient1: '',
gradient2: '',
bg: '',
text: ''
};
switch (data.daily.icon) {
case 'clear-day':
weatherStyle = {
gradient1: '#A5CEF0', gradient2: '#FEF4E0', text: 'black'
};
weatherStyle = { bg: '#FEF4E0', text: 'black' };
break;
case 'clear-night':
weatherStyle = {
gradient1: '#56668e', gradient2: '#000080', text: 'white'
};
weatherStyle = { bg: '#000080', text: 'white' };
break;
case 'rain':
weatherStyle = {
gradient1: '#b1b2b3', gradient2: '#b0c7ff', text: 'black'
};
weatherStyle = { bg: '#b0c7ff', text: 'black' };
break;
case 'snow':
weatherStyle = {
gradient1: '#eee', gradient2: '#f9f9f9', text: 'black'
};
weatherStyle = { bg: '#f9f9f9', text: 'black' };
break;
case 'sleet':
weatherStyle = {
gradient1: '#eee', gradient2: '#f9f9f9', text: 'black'
};
weatherStyle = { bg: '#f9f9f9', text: 'black' };
break;
case 'wind':
weatherStyle = {
gradient1: '#eee', gradient2: '#fff', text: 'black'
};
weatherStyle = { bg: '#fff', text: 'black' };
break;
case 'fog':
weatherStyle = {
gradient1: '#eee', gradient2: '#fff', text: 'black'
};
weatherStyle = { bg: '#fff', text: 'black' };
break;
case 'cloudy':
weatherStyle = {
gradient1: '#eee', gradient2: '#b1b2b3', text: 'black'
};
weatherStyle = { bg: '#b1b2b3', text: 'black' };
break;
case 'partly-cloudy-day':
weatherStyle = {
gradient1: '#fcc440', gradient2: '#b1b2b3', text: 'black'
};
weatherStyle = { bg: '#b1b2b3', text: 'black' };
break;
case 'partly-cloudy-night':
weatherStyle = {
gradient1: '#7f7f7f', gradient2: '#56668e', text: 'white'
};
weatherStyle = { bg: '#56668e', text: 'white' };
break;
default:
weatherStyle = {
gradient1: 'white', gradient2: 'white', text: 'black'
};
weatherStyle = { bg: 'white', text: 'black' };
}
return weatherStyle;
}
// all tile views
renderWrapper(child,
weatherStyle = { gradient1: 'white', gradient2: 'white', text: 'black' }
weatherStyle = { bg: 'white', text: 'black' }
) {
return (
<Tile>
<div
className={'relative br2 ba b--gray3 b--gray2-d ' + weatherStyle.text}
style={{
width: 126,
height: 126,
background: `linear-gradient(135deg, ${weatherStyle.gradient1} 0%,` +
`${weatherStyle.gradient2} 45%, ${weatherStyle.gradient2} 65%,` +
`${weatherStyle.gradient1} 100%)`
}}
>
<Tile bg={weatherStyle.bg} color={weatherStyle.color}>
{child}
</div>
</Tile>
);
}
@ -136,25 +103,25 @@ export default class WeatherTile extends React.Component {
let secureCheck;
let error;
if (this.state.error === true) {
error = <p
className="f9 red2 pt1"
>Please try again.
</p>;
error = <p className="f9 red2 pt1">Please try again.</p>;
}
if (location.protocol === 'https:') {
secureCheck = (
<a className="black white-d f9 absolute pointer"
style={{ right: 8, top: 8 }}
<a className="black white-d f9 pointer"
onClick={() => this.locationSubmit()}>
Detect ->
</a>
);
}
return this.renderWrapper(
<div className="pa2 w-100 h-100 bg-white bg-gray0-d black white-d ">
<Box
display='flex'
flexDirection='column'
justifyContent='space-between'
height='100%'
>
<a
className="f9 black white-d pointer absolute"
style={{ top: 8 }}
className="f9 black white-d pointer"
onClick={() =>
this.setState({ manualEntry: !this.state.manualEntry })
}
@ -162,93 +129,113 @@ export default class WeatherTile extends React.Component {
&lt;&#45;
</a>
{secureCheck}
<p className="f9 pt5">
<Text pb={1} mb='auto'>
Please enter your{' '}
<a
className="black bb white-d"
className="bb"
href="https://latitudeandlongitude.org/"
target="_blank"
>
latitude and longitude
</a>
.
</p>
</Text>
{error}
<div className="absolute" style={{ left: 8, bottom: 8 }}>
<form className="flex" style={{ marginBlockEnd: 0 }}>
<input
id="gps"
className="w-100 black white-d bg-transparent bn f9"
type="text"
placeholder="29.558107, -95.089023"
onKeyDown={(e) => {
if (e.key === 'Enter') {
e.preventDefault();
this.manualLocationSubmit(e.target.value);
}
}} />
<input
className={'bg-transparent black white-d bn pointer ' +
'f9 flex-shrink-0 pr1'}
type="submit"
onClick={() => this.manualLocationSubmit()}
value="->"
/>
</form>
</div>
</div>
<form mt='auto' className="flex" style={{ marginBlockEnd: 0}}>
<input
id="gps"
size="10"
className="w-100 black white-d bg-transparent bn f9"
type="text"
placeholder="29.55, -95.08"
onKeyDown={(e) => {
if (e.key === 'Enter') {
e.preventDefault();
this.manualLocationSubmit(e.target.value);
}
}} />
<input
className={'bg-transparent black white-d bn pointer ' +
'f9 flex-shrink-0 pr1'}
type="submit"
onClick={() => this.manualLocationSubmit()}
value="->"
/>
</form>
</Box>
);
}
renderNoData() {
return this.renderWrapper((
<div
className={'pa2 w-100 h-100 ' +
'bg-white bg-gray0-d black white-d'}
onClick={() => this.setState({ manualEntry: !this.state.manualEntry })}
return this.renderWrapper(
<Box
bg='white'
display='flex'
flexDirection='column'
justifyContent='space-between'
height='100%'
onClick={() => this.setState({ manualEntry: !this.state.manualEntry })}
>
<p className="f9 absolute"
style={{ left: 8, top: 8 }}
>
Weather
</p>
<p className="absolute w-100 flex-col f9"
style={{ bottom: 8, left: 8, cursor: 'pointer' }}
>
-> Set location
</p>
</div>
));
<Text>Weather</Text>
<Text style={{ cursor: 'pointer' }}>
-> Set location
</Text>
</Box>
);
}
renderWithData(data, weatherStyle) {
const c = data.currently;
const d = data.daily.data[0];
const da = moment.unix(d.sunsetTime).format('h:mm a') || '';
const sunset = moment.unix(d.sunsetTime);
const sunsetDiff = sunset.diff(moment(), 'hours');
const sunrise = moment.unix(d.sunriseTime);
let sunriseDiff = sunrise.diff(moment(), 'hours');
if (sunriseDiff > 24) {
sunriseDiff = sunriseDiff - 24;
} else if (sunriseDiff < 0) {
sunriseDiff = sunriseDiff + 24;
}
const nextSolarEvent = sunsetDiff > 0
? `Sun sets in ${sunsetDiff}h`
: `Sun rises in ${sunriseDiff}h`;
return this.renderWrapper(
<div className="w-100 h-100"
style={{ backdropFilter: 'blur(80px)' }}
<Box
width='100%'
height='100%'
display='flex'
flexDirection='column'
alignItems='space-between'
>
<p className="f9 absolute" style={{ left: 8, top: 8 }}>
<Text>
<Icon icon='Weather' color='transparent' stroke='black' display='inline' style={{ position: 'relative', top: '.3em'}} />
Weather
</p>
<a
className="f9 absolute pointer"
style={{ right: 8, top: 8 }}
onClick={() =>
this.setState({ manualEntry: !this.state.manualEntry })
}
>
<a
className='pointer'
onClick={() =>
this.setState({ manualEntry: !this.state.manualEntry })
}
>
->
</a>
<div className="w-100 absolute" style={{ left: 8, bottom: 8 }}>
<p className="f9">{c.summary}</p>
<p className="f9 pt1">{Math.round(c.temperature)}°</p>
<p className="f9 pt1">Sunset at {da}</p>
</div>
</div>
</Text>
<Box
mt="auto"
width="100%"
display="flex"
flexDirection="column"
>
<Text>{c.summary}</Text>
<Text>{Math.round(c.temperature)}°</Text>
<Text>{nextSolarEvent}</Text>
</Box>
</Box>
, weatherStyle);
}
@ -269,14 +256,8 @@ export default class WeatherTile extends React.Component {
<div
className={'pa2 w-100 h-100 ' +
'bg-white bg-gray0-d black white-d'}>
<p className="f9 absolute"
style={{ left: 8, top: 8 }}
>
Weather
</p>
<p className="absolute w-100 flex-col f9"
style={{ bottom: 8, left: 8 }}
>
<Text>Weather</Text>
<p className="w-100 flex-col f9">
Loading, please check again later...
</p>
</div>

View File

@ -1,5 +1,6 @@
import React from 'react';
import { Link } from 'react-router-dom';
import { Box, Text } from '@tlon/indigo-react';
export default class Welcome extends React.Component {
constructor() {
@ -18,25 +19,30 @@ export default class Welcome extends React.Component {
render() {
const firstTime = this.props.firstTime;
return (firstTime && this.state.show) ? (
<div className={'fl ma2 bg-white bg-gray0-d white-d overflow-hidden ' +
'ba b--black b--gray1-d pa2 lh-copy'}
<Box
bg='white'
border={1}
margin={3}
padding={3}
display='flex'
flexDirection='column'
alignItems='flex-start'
>
<p className="f9">Welcome. This virtual computer belongs to you completely. The Urbit ID you used to boot it is yours as well.</p>
<p className="f9 pt2">Since your ID and OS belong to you, its up to you to keep them safe. Be sure your ID is somewhere you wont lose it and you keep your OS on a machine you trust.</p>
<p className="f9 pt2">Urbit OS is designed to keep your data secure and hard to lose. But the system is still young so dont put anything critical in here just yet.</p>
<p className="f9 pt2">To begin exploring, you should probably pop into a chat and verify there are signs of life in this new place. If you were invited by a friend, you probably already have access to a few groups.</p>
<p className="f9 pt2">If you don't know where to go, feel free to
<Link className="no-underline bb b--black b--gray1-d dib" to="/~landscape/join/~bitbet-bolbel/urbit-community">join the Urbit Community group</Link>.
</p>
<p className="f9 pt2">Have fun!</p>
<p className="dib f9 pt2 bb b--black b--gray1-d pointer"
<Text>Welcome. This virtual computer belongs to you completely. The Urbit ID you used to boot it is yours as well.</Text>
<Text pt={2}>Since your ID and OS belong to you, its up to you to keep them safe. Be sure your ID is somewhere you wont lose it and you keep your OS on a machine you trust.</Text>
<Text pt={2}>Urbit OS is designed to keep your data secure and hard to lose. But the system is still young so dont put anything critical in here just yet.</Text>
<Text pt={2}>To begin exploring, you should probably pop into a chat and verify there are signs of life in this new place. If you were invited by a friend, you probably already have access to a few groups.</Text>
<Text pt={2}>If you don't know where to go, feel free to <Link className="no-underline bb b--black b--gray1-d dib" to="/~landscape/join/~bitbet-bolbel/urbit-community">join the Urbit Community group</Link>.
</Text>
<Text pt={2}>Have fun!</Text>
<Text pt={2} className='pointer bb'
onClick={(() => {
this.disableWelcome();
})}
>
Close this note
</p>
</div>
</Text>
</Box>
) : null;
}
}