From f7cd173439206f2dd392263b9b4d4319a397bdaa Mon Sep 17 00:00:00 2001 From: Matilde Park Date: Fri, 16 Oct 2020 12:25:57 -0400 Subject: [PATCH 01/11] publish: prevent input overflow on safari Fixes urbit/landscape#117. --- .../src/views/apps/publish/components/MarkdownEditor.tsx | 1 + .../src/views/apps/publish/components/MarkdownField.tsx | 1 + .../src/views/apps/publish/components/NoteForm.tsx | 8 ++++---- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/pkg/interface/src/views/apps/publish/components/MarkdownEditor.tsx b/pkg/interface/src/views/apps/publish/components/MarkdownEditor.tsx index c31b62913a..74f4274fa0 100644 --- a/pkg/interface/src/views/apps/publish/components/MarkdownEditor.tsx +++ b/pkg/interface/src/views/apps/publish/components/MarkdownEditor.tsx @@ -60,6 +60,7 @@ export function MarkdownEditor( border={1} borderColor="lightGray" borderRadius={2} + height={['calc(100% - 22vh)', '100%']} {...boxProps} > -
- - + + + From 2ca8f41288ae3097838db33fd90997eda428427e Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Mon, 19 Oct 2020 12:45:20 +1000 Subject: [PATCH 02/11] metadata-store: add |remove generator --- pkg/arvo/gen/metadata-store/remove.hoon | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 pkg/arvo/gen/metadata-store/remove.hoon diff --git a/pkg/arvo/gen/metadata-store/remove.hoon b/pkg/arvo/gen/metadata-store/remove.hoon new file mode 100644 index 0000000000..571890d5e0 --- /dev/null +++ b/pkg/arvo/gen/metadata-store/remove.hoon @@ -0,0 +1,20 @@ +:: :metadata-store|remove: remove resource from group +:: Usage: +:: :metadata-store|remove +:: +:: %urbit-community %chat /~darrux-landes/general-503 +:: +:: You can acquire the channel-path with +:: :metadata-store +dbug [%state '(~(got by group-indices) )' +:: and looking for the entry with an app-path that is similar to the +:: title of the channel +:: +/- *metadata-store +/+ resource +:- %say +|= $: [now=@da eny=@uvJ =beak] + [[group=term app=term =path ~] ~] + == +:- %metadata-action +^- metadata-action +[%remove (en-path:resource [p.beak group]) app path] From 835378650bfa1919a964320a68975c9b3fe0f412 Mon Sep 17 00:00:00 2001 From: Matilde Park Date: Tue, 20 Oct 2020 17:22:00 -0400 Subject: [PATCH 03/11] chat: timestamps are lined at top of messages --- pkg/interface/src/views/apps/chat/components/ChatMessage.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/interface/src/views/apps/chat/components/ChatMessage.tsx b/pkg/interface/src/views/apps/chat/components/ChatMessage.tsx index 999e8eda33..00ea0c0f8f 100644 --- a/pkg/interface/src/views/apps/chat/components/ChatMessage.tsx +++ b/pkg/interface/src/views/apps/chat/components/ChatMessage.tsx @@ -97,7 +97,7 @@ export default class ChatMessage extends Component { const containerClass = `${renderSigil ? `cf pt2 pl3 lh-copy` - : `items-center cf hide-child`} ${isPending ? 'o-40' : ''} ${className}` + : `items-top cf hide-child`} ${isPending ? 'o-40' : ''} ${className}` const timestamp = moment.unix(msg.when / 1000).format(renderSigil ? 'hh:mm a' : 'hh:mm'); @@ -255,7 +255,7 @@ export class MessageWithSigil extends PureComponent { export const MessageWithoutSigil = ({ timestamp, msg, remoteContentPolicy, measure }) => ( <> -

{timestamp}

+ {timestamp} From b97f94a56a3d6568463a6fc03f51d682ac704320 Mon Sep 17 00:00:00 2001 From: Matilde Park Date: Wed, 21 Oct 2020 13:17:22 -0400 Subject: [PATCH 04/11] chat: hoon eval preserves whitespace --- pkg/interface/src/views/apps/chat/components/content/code.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkg/interface/src/views/apps/chat/components/content/code.js b/pkg/interface/src/views/apps/chat/components/content/code.js index e0a97a818e..3a133e06de 100644 --- a/pkg/interface/src/views/apps/chat/components/content/code.js +++ b/pkg/interface/src/views/apps/chat/components/content/code.js @@ -19,6 +19,7 @@ export default class CodeContent extends Component { overflow='auto' maxHeight='10em' maxWidth='100%' + style={{ whiteSpace: 'pre' }} backgroundColor='scales.black10' > {content.code.output[0].join('\n')} @@ -36,6 +37,7 @@ export default class CodeContent extends Component { overflow='auto' maxHeight='10em' maxWidth='100%' + style={{ whiteSpace: 'pre' }} > {content.code.expression} From 47c0afae6918b336bbdbc241fd0532805df36982 Mon Sep 17 00:00:00 2001 From: Matilde Park Date: Wed, 21 Oct 2020 14:55:59 -0400 Subject: [PATCH 05/11] chat: pad unread marker more evenly --- .../views/apps/chat/components/ChatMessage.tsx | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/pkg/interface/src/views/apps/chat/components/ChatMessage.tsx b/pkg/interface/src/views/apps/chat/components/ChatMessage.tsx index 999e8eda33..0ad1f275b5 100644 --- a/pkg/interface/src/views/apps/chat/components/ChatMessage.tsx +++ b/pkg/interface/src/views/apps/chat/components/ChatMessage.tsx @@ -1,7 +1,7 @@ import React, { Component, PureComponent } from "react"; import moment from "moment"; import _ from "lodash"; -import { Box, Row, Text } from "@tlon/indigo-react"; +import { Box, Row, Text, Rule } from "@tlon/indigo-react"; import { OverlaySigil } from './overlay-sigil'; import { uxToHex, cite, writeText } from '~/logic/lib/util'; @@ -14,15 +14,15 @@ import RemoteContent from '~/views/components/RemoteContent'; export const DATESTAMP_FORMAT = '[~]YYYY.M.D'; export const UnreadMarker = React.forwardRef(({ dayBreak, when }, ref) => ( -
-
-

New messages below

-
+ + + New messages below + {dayBreak - ?

{moment(when).calendar()}

+ ? {moment(when).calendar()} : null} -
-
+ +
)); export const DayBreak = ({ when }) => ( @@ -125,7 +125,7 @@ export default class ChatMessage extends Component { }; const unreadContainerStyle = { - height: isLastRead ? '1.66em' : '0', + height: isLastRead ? '2rem' : '0', }; return ( From be959df8dfe4f3d5f4dafaac7374aacb68edfce7 Mon Sep 17 00:00:00 2001 From: Matilde Park Date: Wed, 21 Oct 2020 15:23:36 -0400 Subject: [PATCH 06/11] launch: migrate remainder to indigo-react Fixes urbit/landscape#53. --- pkg/interface/src/views/apps/launch/app.js | 21 ++--- .../apps/launch/components/tiles/basic.js | 3 +- .../apps/launch/components/tiles/custom.js | 32 ++++--- .../apps/launch/components/tiles/weather.js | 87 +++++++++++-------- 4 files changed, 80 insertions(+), 63 deletions(-) diff --git a/pkg/interface/src/views/apps/launch/app.js b/pkg/interface/src/views/apps/launch/app.js index f2e09aeebd..090026d45f 100644 --- a/pkg/interface/src/views/apps/launch/app.js +++ b/pkg/interface/src/views/apps/launch/app.js @@ -1,24 +1,21 @@ import React from 'react'; import Helmet from 'react-helmet'; -import { Link } from 'react-router-dom'; import { Box, Row, Icon, Text, Center } from '@tlon/indigo-react'; -import { uxToHex, adjustHex } from "~/logic/lib/util"; +import { uxToHex, adjustHex } from '~/logic/lib/util'; import './css/custom.css'; -import { Sigil } from "~/logic/lib/sigil"; +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'; export default class LaunchApp extends React.Component { - componentDidMount() { // preload spinner asset new Image().src = '/~landscape/img/Spinner.png'; - } render() { @@ -27,16 +24,15 @@ export default class LaunchApp extends React.Component { const sigilColor = contact?.color ? `#${uxToHex(contact.color)}` : props.dark - ? "#FFFFFF" - : "#000000"; - + ? '#FFFFFF' + : '#000000'; return ( <> OS1 - Home -
+ - + + p={2} + > {props.baseHash} -
+ ); } diff --git a/pkg/interface/src/views/apps/launch/components/tiles/basic.js b/pkg/interface/src/views/apps/launch/components/tiles/basic.js index c15aaa40e0..c511432a81 100644 --- a/pkg/interface/src/views/apps/launch/components/tiles/basic.js +++ b/pkg/interface/src/views/apps/launch/components/tiles/basic.js @@ -1,5 +1,4 @@ import React from 'react'; -import classnames from 'classnames'; import { Text, Icon } from '@tlon/indigo-react'; import Tile from './tile'; @@ -23,7 +22,7 @@ export default class BasicTile extends React.PureComponent { verticalAlign='top' pt='5px' pr='2px' - /> + /> : null }{props.title} diff --git a/pkg/interface/src/views/apps/launch/components/tiles/custom.js b/pkg/interface/src/views/apps/launch/components/tiles/custom.js index 7a5eb4690d..47403b3487 100644 --- a/pkg/interface/src/views/apps/launch/components/tiles/custom.js +++ b/pkg/interface/src/views/apps/launch/components/tiles/custom.js @@ -1,26 +1,30 @@ import React from 'react'; -import classnames from 'classnames'; - +import { Box, BaseImage } from '@tlon/indigo-react'; import Tile from './tile'; export default class CustomTile extends React.PureComponent { - render() { - const { props } = this; - return ( -
- + -
+ src='/~launch/img/UnknownCustomTile.png' + width='48px' + height='48px' + /> +
); } - } diff --git a/pkg/interface/src/views/apps/launch/components/tiles/weather.js b/pkg/interface/src/views/apps/launch/components/tiles/weather.js index 545c780fc0..64140b2711 100644 --- a/pkg/interface/src/views/apps/launch/components/tiles/weather.js +++ b/pkg/interface/src/views/apps/launch/components/tiles/weather.js @@ -1,6 +1,6 @@ import React from 'react'; import moment from 'moment'; -import { Box, Icon, Text } from '@tlon/indigo-react'; +import { Box, Icon, Text, BaseAnchor, BaseInput } from '@tlon/indigo-react'; import Tile from './tile'; @@ -103,14 +103,15 @@ export default class WeatherTile extends React.Component { let secureCheck; let error; if (this.state.error === true) { - error =

Please try again.

; + error = Please try again.; } if (location.protocol === 'https:') { secureCheck = ( - this.locationSubmit()}> + this.locationSubmit()} + > Detect -> - + ); } return this.renderWrapper( @@ -120,32 +121,38 @@ export default class WeatherTile extends React.Component { justifyContent='space-between' height='100%' > - this.setState({ manualEntry: !this.state.manualEntry }) } > <- - + {secureCheck} Please enter your{' '} - latitude and longitude - + . {error} - - + { @@ -153,15 +160,21 @@ export default class WeatherTile extends React.Component { e.preventDefault(); this.manualLocationSubmit(e.target.value); } - }} /> - + this.manualLocationSubmit()} value="->" /> - + ); } @@ -216,17 +229,17 @@ export default class WeatherTile extends React.Component { alignItems='space-between' > - + Weather - - this.setState({ manualEntry: !this.state.manualEntry }) - } - > - -> - + + this.setState({ manualEntry: !this.state.manualEntry }) + } + > + -> + + Weather -

+ Loading, please check again later... -

- + +
)); } return this.renderNoData(); From 18b46484cff0223e8624b53dbdad8d4c81fbd6a8 Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Thu, 22 Oct 2020 10:42:36 +1000 Subject: [PATCH 07/11] GroupsPane: handle new channel route correctly on mobile --- pkg/interface/src/views/landscape/components/GroupsPane.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/interface/src/views/landscape/components/GroupsPane.tsx b/pkg/interface/src/views/landscape/components/GroupsPane.tsx index b8f91a618c..2029ba2311 100644 --- a/pkg/interface/src/views/landscape/components/GroupsPane.tsx +++ b/pkg/interface/src/views/landscape/components/GroupsPane.tsx @@ -168,7 +168,7 @@ export function GroupsPane(props: GroupsPaneProps) { render={(routeProps) => { const newUrl = `${baseUrl}/new`; return ( - + Date: Thu, 22 Oct 2020 10:43:14 +1000 Subject: [PATCH 08/11] NewChannel: redirect on successful creation --- pkg/interface/src/logic/lib/util.js | 4 ++++ .../src/views/landscape/components/NewChannel.tsx | 14 ++++++++------ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/pkg/interface/src/logic/lib/util.js b/pkg/interface/src/logic/lib/util.js index fe3e07d760..8a1a4b8da0 100644 --- a/pkg/interface/src/logic/lib/util.js +++ b/pkg/interface/src/logic/lib/util.js @@ -3,6 +3,10 @@ import f from 'lodash/fp'; export const MOBILE_BROWSER_REGEX = /Android|webOS|iPhone|iPad|iPod|BlackBerry/i; +export function parentPath(path) { + return _.dropRight(path.split('/'), 1).join('/'); +} + export function clamp(x,min,max) { return Math.max(min, Math.min(max, x)); } diff --git a/pkg/interface/src/views/landscape/components/NewChannel.tsx b/pkg/interface/src/views/landscape/components/NewChannel.tsx index 7224d0b3db..d563ffcaab 100644 --- a/pkg/interface/src/views/landscape/components/NewChannel.tsx +++ b/pkg/interface/src/views/landscape/components/NewChannel.tsx @@ -12,20 +12,19 @@ import GlobalApi from "~/logic/api/global"; import { AsyncButton } from "~/views/components/AsyncButton"; import { FormError } from "~/views/components/FormError"; import { RouteComponentProps } from "react-router-dom"; -import { stringToSymbol } from "~/logic/lib/util"; +import { stringToSymbol, parentPath } from "~/logic/lib/util"; import GroupSearch from "~/views/components/GroupSearch"; import { Associations } from "~/types/metadata-update"; import { useWaitForProps } from "~/logic/lib/useWaitForProps"; -import { Notebooks } from "~/types/publish-update"; import { Groups } from "~/types/group-update"; import { ShipSearch } from "~/views/components/ShipSearch"; -import { Rolodex } from "~/types"; +import { Rolodex, Workspace } from "~/types"; interface FormSchema { name: string; description: string; ships: string[]; - type: "chat" | "publish" | "links"; + type: "chat" | "publish" | "link"; } const formSchema = Yup.object({ @@ -41,6 +40,7 @@ interface NewChannelProps { contacts: Rolodex; groups: Groups; group?: string; + workspace: Workspace; } @@ -72,7 +72,7 @@ export function NewChannel(props: NewChannelProps & RouteComponentProps) { case "publish": await props.api.publish.newBook(resId, name, description, group); break; - case "links": + case "link": if (group) { await api.graph.createManagedGraph( resId, @@ -100,6 +100,8 @@ export function NewChannel(props: NewChannelProps & RouteComponentProps) { await waiter((p) => !!p?.groups?.[`/ship/~${window.ship}/${resId}`]); } actions.setStatus({ success: null }); + const resourceUrl = parentPath(location.pathname); + history.push(`${resourceUrl}/resource/${type}${type === 'link' ? '/ship' : ''}/~${window.ship}/${resId}`); } catch (e) { console.error(e); actions.setStatus({ error: "Channel creation failed" }); @@ -132,7 +134,7 @@ export function NewChannel(props: NewChannelProps & RouteComponentProps) { Channel Type - + Date: Thu, 22 Oct 2020 14:02:57 -0400 Subject: [PATCH 09/11] landscape: prevent thick horizontal scroll --- pkg/interface/src/views/App.js | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/interface/src/views/App.js b/pkg/interface/src/views/App.js index c19e0a78a1..87dc0f57a6 100644 --- a/pkg/interface/src/views/App.js +++ b/pkg/interface/src/views/App.js @@ -49,6 +49,7 @@ const Root = styled.div` /* Works on Chrome/Edge/Safari */ *::-webkit-scrollbar { width: 6px; + height: 6px; } *::-webkit-scrollbar-track { background: transparent; From 871336b40203ee57b99d48e4053a06c6b4e07aeb Mon Sep 17 00:00:00 2001 From: Tyler Brown Cifu Shuster Date: Wed, 21 Oct 2020 10:36:39 -0700 Subject: [PATCH 10/11] clock: render in SVG, show moon phase --- .../apps/launch/components/tiles/clock.js | 507 +++++++----------- 1 file changed, 205 insertions(+), 302 deletions(-) diff --git a/pkg/interface/src/views/apps/launch/components/tiles/clock.js b/pkg/interface/src/views/apps/launch/components/tiles/clock.js index dd0a4c79f7..da0cf2ad16 100644 --- a/pkg/interface/src/views/apps/launch/components/tiles/clock.js +++ b/pkg/interface/src/views/apps/launch/components/tiles/clock.js @@ -1,38 +1,28 @@ import React from 'react'; import moment from 'moment'; import SunCalc from 'suncalc'; +import styled from 'styled-components'; import Tile from './tile'; -const innerSize = 124; // clock size +const VIEWBOX_SIZE = 100; +const CX = VIEWBOX_SIZE / 2; +const CY = VIEWBOX_SIZE / 2; +const RADIUS = VIEWBOX_SIZE / 2; +const CELESTIAL_BODY_SIZE = 16; -// polar to cartesian -// var ptc = function(r, theta) { -// return { -// x: r * Math.cos(theta), -// y: r * Math.sin(theta) -// } -// } - -let timeTextColor = '#000000', dateTextColor = '#333333', background = '#ffffff'; - -const dark = window.matchMedia('(prefers-color-scheme: dark)'); - -if (dark.matches) { - timeTextColor = '#ffffff'; - dateTextColor = '#7f7f7f'; - background = '#333'; -} - -function darkColors(dark) { - if (dark.matches) { - timeTextColor = '#ffffff'; - dateTextColor = '#7f7f7f'; - background = '#ffffff'; +const ApplyClockBg = styled.div` + .background { + fill: ${p => p.theme.colors.white}; } - } - -dark.addListener(darkColors); + .time { + fill: ${p => p.theme.colors.black}; + color: ${p => p.theme.colors.black}; + } + .date { + fill: ${p => p.theme.colors.gray}; + } +`; const toRelativeTime = (date, referenceTime, unit) => moment(date) .diff(referenceTime, unit); @@ -42,10 +32,6 @@ const minsToDegs = (mins) => { return (mins / 1440) * 360; }; -const splitArc = (start, end) => end + ((start - end) * 0.5); - -const isOdd = n => Math.abs(n % 2) == 1; - const radToDeg = rad => rad * (180 / Math.PI); const degToRad = deg => deg * (Math.PI / 180); @@ -54,58 +40,137 @@ const convert = (date, referenceTime) => { return minsToDegs(toRelativeTime(date, referenceTime, 'minutes')); }; -const circle = (ctx, x, y, r, from, to, fill) => { - ctx.beginPath(); - ctx.arc( x, y, r, from, to ); - ctx.strokeStyle = 'rgba(0,0,0,0)'; - ctx.fillStyle = fill || 'rgba(0,0,0,0)'; - 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)'; - if (lineWidth) { - ctx.stroke(); +// https://github.com/tingletech/moon-phase +export const dFromPhase = (moonPhase) => { + let mag, sweep, d = "m50,0"; + if (moonPhase <= 0.25) { + sweep = [ 1, 0 ]; + mag = 20 - 20 * moonPhase * 4; + } else if (moonPhase <= 0.50) { + sweep = [ 0, 0 ]; + mag = 20 * (moonPhase - 0.25) * 4; + } else if (moonPhase <= 0.75) { + sweep = [ 1, 1 ]; + mag = 20 - 20 * (moonPhase - 0.50) * 4; + } else if (moonPhase <= 1) { + sweep = [ 0, 1 ]; + mag = 20 * (moonPhase - 0.75) * 4; } -}; -const arc = (ctx, x, y, r, from, to, fill) => { - ctx.beginPath(); - ctx.arc( x, y, r, from, to ); - ctx.fillStyle = 'rgba(0,0,0,0)'; - ctx.lineWidth = r * 2; - ctx.strokeStyle = fill || 'rgba(0,0,0,0)'; - ctx.stroke(); -}; + d = d + "a" + mag + ",20 0 1," + sweep[0] + " 0,100 "; + d = d + "a20,20 0 1," + sweep[1] + " 0,-100"; + return d; +} -const degArc = (ctx, x, y, r, from, to, fill) => { - ctx.beginPath(); - ctx.arc( x, y, r, degToRad(from), degToRad(to)); - ctx.fillStyle = 'rgba(0,0,0,0)'; - ctx.lineWidth = r * 2; - ctx.strokeStyle = fill || 'rgba(0,0,0,0)'; - ctx.stroke(); -}; +const Moon = ({ angle, ...props }) => { + const phase = SunCalc.getMoonIllumination(moment().toDate()).phase.toFixed(2); + const cx = CX + (RADIUS - 12) * Math.cos(degToRad(angle)) - (CELESTIAL_BODY_SIZE / 2); + const cy = CY + (RADIUS - 12) * Math.sin(degToRad(angle)) - (CELESTIAL_BODY_SIZE / 2); + return ( + + + + + + + + ); +} -class Clock extends React.Component { +const Sun = ({ angle, ...props}) => ( + +); + +const SvgArc = ({ start, end, ...rest }) => { + const x1 = CX + RADIUS * Math.cos(degToRad(start)); + const y1 = CY + RADIUS * Math.sin(degToRad(start)); + const x2 = CX + RADIUS * Math.cos(degToRad(end)); + const y2 = CY + RADIUS * Math.sin(degToRad(end)); + + const isLarge = Math.abs((start > 360 ? start - 360 : start) - end) > 180; + + const d = [ + 'M', CX, CY, + 'L', x1, y1, + 'A', RADIUS, RADIUS, '0', (isLarge ? '1' : '0'), '1', x2, y2, 'z' + ].join(' '); + + return ; +} + +class ClockText extends React.Component { + constructor(props) { + super(props); + this.state = { + time: Date.now() + } + } + componentDidMount() { + this.interval = setInterval(() => this.setState({ time: Date.now() }), 1000); + } + + componentWillUnmount() { + clearInterval(this.interval); + } + + render() { + const now = moment(this.state.time); + return ( + <> + + + {now.format('h')} + : + + {now.format('mm A')} + + {now.format('MMM D')}{now.format('Do').replace(now.format('D'), '')} + + ); + } +} + +class Clock extends React.PureComponent { constructor(props) { super(props); - this.animate = this.animate.bind(this); - this.canvasRef = React.createRef(); - this.canvas = null; this.angle = 0; this.referenceTime = moment().startOf('day').subtract(6, 'hours'); this.state = { + time: Date.now(), lat: 0, lon: 0, geolocationSuccess: false, @@ -164,246 +229,84 @@ class Clock extends React.Component { } componentDidMount() { - this.canvas = initCanvas( - this.canvasRef, - { x: innerSize, y: innerSize }, - 4 - ); - this.initGeolocation(); - this.animate(); + this.interval = setInterval(() => this.setState({ time: Date.now() }), 60000); } componentWillUnmount() { - if (this.animationTimer) { - window.clearTimeout(this.animationTimer); - } - } - - animate() { - this.animationTimer = - window.setTimeout(() => window.requestAnimationFrame(this.animate), 1000); - - const { state } = this; - const time = new Date(); - const ctx = this.canvas.getContext('2d'); - ctx.clearRect(0, 0, ctx.width, ctx.height); - ctx.save(); - - const ctr = innerSize / 2; - - // Sun+moon calculations - const cx = ctr; - const cy = ctr; - this.angle = degToRad(convert(time, this.referenceTime)); - const newX = cx + (ctr - 15) * Math.cos(this.angle); - const newY = cy + (ctr - 15) * Math.sin(this.angle); - - // Center white circle with time and date - circle( - ctx, - ctr, - ctr, - ctr, - -1, - 2 * Math.PI, - background - ); - - // Day - degArc( - ctx, - ctr, - ctr, - ctr / 2, - state.sunriseEnd, - state.sunset, - 'rgba(33, 157, 255, .2)' - ); - - // Sunrise - degArc( - ctx, - ctr, - ctr, - ctr / 2, - state.sunsetStart, - state.sunriseEnd, - '#FFC700' - ); - - // Sunset - degArc( - ctx, - ctr, - ctr, - ctr / 2, - state.dusk, - state.dawn, - 'rgba(255, 65, 54, .8)' - ); - - // Night - degArc( - ctx, - ctr, - ctr, - ctr / 2, - state.night, - state.nightEnd, - 'rgba(0, 0, 0, .8)' - ); - - if ( - radToDeg(this.angle) > splitArc(state.sunriseEnd, state.nightEnd) - && radToDeg(this.angle) < splitArc(state.sunset, state.night) - ) { - // Sun circle - circle( - ctx, - newX-1/2, - newY-1/2, - 8, - 0, - 2 * Math.PI, - '#FCC440' - ); - - // Sun circle border - circleOutline( - ctx, - newX-1/2, - newY-1/2, - 8, - 0, - 2 * Math.PI, - 'rgba(0,0,0,0.1)', - 1 - ); - } else { - // Moon circle - circle( - ctx, - newX-1/2, - newY-1/2, - 8, - 0, - 2 * Math.PI, - '#FFFFFF' - ); - // Moon circle outline - circleOutline( - ctx, - newX-1/2, - newY-1/2, - 8, - 0, - 2 * Math.PI, - '#000000', - 1 - ); - } - - - // Outer borders - circleOutline( - ctx, - ctr, - ctr, - ctr-1, - -1, - 2 * Math.PI, - 'none', - 0 - ); - - // Center white circle border - circleOutline( - ctx, - ctr, - ctr, - ctr/1.85, - -1, - 2 * Math.PI, - 'none', - 0 - ); - - // Inner hole - circle( - ctx, - ctr, - ctr, - ctr/1.85, - -1, - 2 * Math.PI, - background - ); - - // Text for time and date - const timeText = isOdd(time.getSeconds()) - ? moment().format('h mm A') - : moment().format('h:mm A'); - const dateText = moment().format('MMM Do'); - ctx.textAlign = 'center'; - ctx.fillStyle = timeTextColor; - ctx.font = '12px Inter'; - ctx.fillText(timeText, ctr, ctr + 6 - 7); - ctx.fillStyle = dateTextColor; - ctx.font = '12px Inter'; - ctx.fillText(dateText, ctr, ctr + 6 + 7); - - ctx.restore(); + clearInterval(this.interval); } render() { + const now = moment(this.state.time); + const angle = convert(now, this.referenceTime); + return ( - this.canvasRef = canvasRef } - id="clock-canvas" - /> + + + + + + + + + + + + + + + + + + + + + + + {angle > this.state.nightEnd && angle < this.state.sunset + ? + : + } + + + + ); } } -export default class ClockTile extends React.Component { - constructor(props) { - super(props); - } - renderWrapper(child) { - return ( - - {child} - - ); - } - render() { - const data = this.props.location ? this.props.location : {}; - return this.renderWrapper(( - - )); - } -} - -const initCanvas = (canvas, size, ratio) => { - const { x, y } = size; - // let ratio = ctx.webkitBackingStorePixelRatio < 2 - // ? window.devicePixelRatio - // : 1; - - // default for high print resolution. - // ratio = ratio * resMult; - - canvas.width = x * ratio; - canvas.height = y * ratio; - canvas.style.width = x + 'px'; - canvas.style.height = y + 'px'; - - canvas.getContext('2d').scale(ratio, ratio); - - return canvas; -}; +const ClockTile = ({ location = {} }) => ( + + + +); +export default ClockTile; \ No newline at end of file From 91738280aeaec5ead0754586b0561f8d4ead6ece Mon Sep 17 00:00:00 2001 From: Tyler Brown Cifu Shuster Date: Wed, 21 Oct 2020 19:41:15 -0700 Subject: [PATCH 11/11] chat: fall back to native input on mobile --- .../views/apps/chat/components/chat-editor.js | 55 +++++++++++++++++-- 1 file changed, 49 insertions(+), 6 deletions(-) diff --git a/pkg/interface/src/views/apps/chat/components/chat-editor.js b/pkg/interface/src/views/apps/chat/components/chat-editor.js index a81f265f79..810c0be098 100644 --- a/pkg/interface/src/views/apps/chat/components/chat-editor.js +++ b/pkg/interface/src/views/apps/chat/components/chat-editor.js @@ -3,7 +3,7 @@ import { UnControlled as CodeEditor } from 'react-codemirror2'; import { MOBILE_BROWSER_REGEX } from "~/logic/lib/util"; import CodeMirror from 'codemirror'; -import { Row } from '@tlon/indigo-react'; +import { Row, BaseInput } from '@tlon/indigo-react'; import 'codemirror/mode/markdown/markdown'; import 'codemirror/addon/display/placeholder'; @@ -36,6 +36,25 @@ const MARKDOWN_CONFIG = { } }; +// Until CodeMirror supports options.inputStyle = 'textarea' on mobile, +// we need to hack this into a regular input that has some funny behaviors +const inputProxy = (input) => new Proxy(input, { + get(target, property) { + if (property in target) { + return target[property]; + } + if (property === 'setOption') { + return () => {}; + } + if (property === 'getValue') { + return () => target.value; + } + if (property === 'setValue') { + return (val) => target.value = val; + } + } +}); + export default class ChatEditor extends Component { constructor(props) { super(props); @@ -129,7 +148,11 @@ export default class ChatEditor extends Component { 'Esc': () => { this.editor?.getInputField().blur(); } - } + }, + // The below will ony work once codemirror's bug is fixed + spellcheck: !!MOBILE_BROWSER_REGEX.test(navigator.userAgent), + autocorrect: !!MOBILE_BROWSER_REGEX.test(navigator.userAgent), + autocapitalize: !!MOBILE_BROWSER_REGEX.test(navigator.userAgent) }; return ( @@ -142,19 +165,39 @@ export default class ChatEditor extends Component { paddingTop='8px' width='calc(100% - 88px)' className={inCodeMode ? 'chat code' : 'chat'} + color="black" > - { + if (event.key === 'Enter') { + this.submit(); + } else { + this.messageChange(null, null, event.target.value); + } + }} + ref={input => { + if (!input) return; + this.editor = inputProxy(input); + }} + {...props} + /> + : this.messageChange(e, d, v)} editorDidMount={(editor) => { this.editor = editor; - if (!MOBILE_BROWSER_REGEX.test(navigator.userAgent)) { - editor.focus(); - } + editor.focus(); }} {...props} /> + } +
); }