launch: more or less up to feature parity, all api actions tested

This commit is contained in:
Logan Allen 2020-05-20 20:43:43 -04:00
parent 0c2b770411
commit e859ec4520
29 changed files with 215 additions and 21747 deletions

View File

@ -8430,6 +8430,11 @@
"object-assign": "^4.1.1"
}
},
"suncalc": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/suncalc/-/suncalc-1.8.0.tgz",
"integrity": "sha1-HZiYEJVjB4dQ9JlKlZ5lTYdqy/U="
},
"supports-color": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz",

View File

@ -23,6 +23,7 @@
"style-loader": "^1.2.1",
"styled-components": "^5.1.0",
"styled-system": "^5.1.5",
"suncalc": "^1.8.0",
"urbit-ob": "^5.0.0",
"urbit-sigil-js": "^1.3.2"
},

View File

@ -5,7 +5,7 @@ import './css/indigo-static.css';
import './css/fonts.css';
import { light } from '@tlon/indigo-react';
import LaunchApp from './apps/launch/LaunchApp';
import LaunchApp from './apps/launch/app';
import ChatApp from './apps/chat/ChatApp';
import DojoApp from './apps/dojo/DojoApp';
import StatusBar from './components/StatusBar';

View File

@ -14,15 +14,15 @@ class PrivateHelper extends BaseApi {
}
launchChangeOrder(orderedTiles = []) {
this.launchAction({ changeOrder: orderedTiles });
this.launchAction({ 'change-order': orderedTiles });
}
launchChangeFirstTime(firstTime = true) {
this.launchAction({ changeFirstTime: firstTime });
this.launchAction({ 'change-first-time': firstTime });
}
launchChangeIsShown(isShown = true) {
this.launchAction({ isShown });
launchChangeIsShown(name, isShown = true) {
this.launchAction({ 'change-is-shown': { name, isShown }});
}
}

View File

@ -6,6 +6,9 @@ import LaunchSubscription from '../../subscription/launch';
import './css/custom.css';
import Tiles from './components/tiles';
import Welcome from './components/welcome';
export default class LaunchApp extends React.Component {
constructor(props) {
@ -33,6 +36,7 @@ export default class LaunchApp extends React.Component {
this.subscription = new LaunchSubscription(this.store, this.api, channel);
this.subscription.start();
window.api = this.api;
}
componentWillUnmount() {
@ -42,8 +46,17 @@ export default class LaunchApp extends React.Component {
}
render() {
const { state } = this;
return (
<div></div>
<div style={{ height: 'calc(100vh - 45px)'}}
className='h-100 w-100 bg-gray0-d'>
<Welcome firstTime={state.launch.firstTime} api={this.api} />
<Tiles
tiles={state.launch.tiles}
tileOrdering={state.launch.tileOrdering}
api={this.api} />
</div>
);
}
}

View File

@ -0,0 +1,46 @@
import React from 'react';
import classnames from 'classnames';
import BasicTile from './tiles/basic';
import CustomTile from './tiles/custom';
import ClockTile from './tiles/clock';
import WeatherTile from './tiles/weather';
export default class Tiles extends React.PureComponent {
render() {
const { props } = this;
console.log('render');
let tiles = props.tileOrdering.filter((key) => {
return props.tiles[key].isShown;
}).map((key) => {
let tile = props.tiles[key];
if ('basic' in tile.type) {
let basic = tile.type.basic;
return (
<BasicTile
key={key}
title={basic.title}
iconUrl={basic.iconUrl}
linkedUrl={basic.linkedUrl} />
);
} else if ('custom' in tile.type) {
if (key === 'weather') {
return (<WeatherTile key={key} api={props.api} />);
} else if (key === 'clock') {
return (<ClockTile key={key} />);
}
} else {
return <CustomTile key={key} />;
}
});
return (
<div>{tiles}</div>
);
}
}

View File

@ -0,0 +1,32 @@
import React from 'react';
import classnames from 'classnames';
import Tile from './tile';
export default class BasicTile extends React.PureComponent {
render() {
const { props } = this;
return (
<Tile>
<div className={"w-100 h-100 relative bg-white bg-gray0-d ba " +
"b--black b--gray1-d"}>
<a className="w-100 h-100 db pa2 no-underline"
href={props.linkedUrl}>
<p className="black white-d absolute f9"
style={{left: 8, top: 8}}>{props.title}</p>
<img
className="absolute invert-d"
style={{ left: 39, top: 39 }}
src={props.iconUrl}
width={48}
height={48} />
</a>
</div>
</Tile>
);
}
}

View File

@ -1,7 +1,9 @@
import React, { Component } from 'react';
import React from 'react';
import classnames from 'classnames';
import moment from 'moment'
import SunCalc from 'suncalc'
import moment from 'moment';
import SunCalc from 'suncalc';
import Tile from './tile';
const outerSize = 124; //tile size
const innerSize = 124; //clock size
@ -95,7 +97,7 @@ const degArc = (ctx, x, y, r, from, to, fill) => {
ctx.stroke();
}
class Clock extends Component {
class Clock extends React.Component {
constructor(props) {
super(props);
@ -396,19 +398,16 @@ class Clock extends Component {
}
}
export default class ClockTile extends Component {
export default class ClockTile extends React.Component {
constructor(props) {
super(props);
}
renderWrapper(child) {
return (
<div className="bg-white bg-gray0-d" style={{
width: outerSize,
height: outerSize,
}}>
<Tile>
{child}
</div>
</Tile>
);
}
@ -452,4 +451,3 @@ const initCanvas = (canvas, size, ratio) => {
return canvas;
}
window.clockTile = ClockTile;

View File

@ -0,0 +1,26 @@
import React from 'react';
import classnames from 'classnames';
import Tile from './tile';
export default class CustomTile extends React.PureComponent {
render() {
const { props } = this;
return (
<Tile>
<div className={"w-100 h-100 relative bg-white bg-gray0-d ba " +
"b--black b--gray1-d"}>
<img
className="absolute invert-d"
style={{ left: 39, top: 39 }}
src={'/~launch/img/UnknownCustomTile.png'}
width={48}
height={48} />
</div>
</Tile>
);
}
}

View File

@ -0,0 +1,13 @@
import React from 'react';
export default class Tile extends React.Component {
render() {
return (
<div className="fl ma2 bg-white bg-gray0-d overflow-hidden"
style={{ height: '126px', width: '126px' }}>
{this.props.children}
</div>
);
}
}

View File

@ -1,8 +1,10 @@
import React, { Component } from 'react';
import React from 'react';
import classnames from 'classnames';
import moment from 'moment';
export default class WeatherTile extends Component {
import Tile from './tile';
export default class WeatherTile extends React.Component {
constructor(props) {
super(props);
this.state = {
@ -10,7 +12,8 @@ export default class WeatherTile extends Component {
manualEntry: false,
error: false
};
let api = window.api;
let api = props.api;
}
// geolocation and manual input functions
locationSubmit() {
@ -115,6 +118,7 @@ export default class WeatherTile extends Component {
weatherStyle = { gradient1: "white", gradient2: "white", text: "black" }
) {
return (
<Tile>
<div
className={"relative " + weatherStyle.text}
style={{
@ -126,6 +130,7 @@ export default class WeatherTile extends Component {
}}>
{child}
</div>
</Tile>
);
}

View File

@ -1,6 +1,6 @@
import React, { Component } from 'react'
import React from 'react'
export class Welcome extends Component {
export default class Welcome extends React.Component {
constructor() {
super();
this.state = {
@ -10,16 +10,15 @@ export class Welcome extends Component {
}
disableWelcome() {
window.api.action("launch", "json", "disable welcome message");
this.props.api.launch.changeFirstTime(false);
this.setState({ show: false });
}
render() {
let firstTime = window.startupMessage;
return (firstTime && this.state.show)
? (
let 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 w-100 lh-copy"}>
"ba b--black b--gray1-d pa2 lh-copy"}>
<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>
@ -32,9 +31,7 @@ export class Welcome extends Component {
Close this note
</p>
</div>
)
: ( null)
) : null;
}
}
export default Welcome

View File

@ -1,63 +0,0 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
*.swp
.DS_Store
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# TypeScript v1 declaration files
typings/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
# next.js build output
.next

View File

@ -1,149 +0,0 @@
var gulp = require('gulp');
var cssimport = require('gulp-cssimport');
var rollup = require('gulp-better-rollup');
var cssnano = require('cssnano');
var autoprefixer = require('autoprefixer');
var postcss = require('gulp-postcss')
var sucrase = require('@sucrase/gulp-plugin');
var minify = require('gulp-minify');
var exec = require('child_process').exec;
var rename = require('gulp-rename');
var del = require('del');
var resolve = require('rollup-plugin-node-resolve');
var commonjs = require('rollup-plugin-commonjs');
var replace = require('rollup-plugin-replace');
var json = require('rollup-plugin-json');
var builtins = require('@joseph184/rollup-plugin-node-builtins');
var rootImport = require('rollup-plugin-root-import');
var globals = require('rollup-plugin-node-globals');
/***
Main config options
***/
var urbitrc = require('../urbitrc');
/***
End main config options
***/
gulp.task('jsx-transform', function (cb) {
return gulp.src('src/**/*.js')
.pipe(sucrase({
transforms: ['jsx']
}))
.pipe(gulp.dest('dist'));
});
gulp.task('tile-jsx-transform', function (cb) {
return gulp.src('tile/**/*.js')
.pipe(sucrase({
transforms: ['jsx']
}))
.pipe(gulp.dest('dist'));
});
gulp.task('js-imports', function (cb) {
return gulp.src('dist/index.js')
.pipe(rollup({
plugins: [
commonjs({
namedExports: {
'node_modules/react/index.js': ['Component'],
'node_modules/react-is/index.js': ['isValidElementType'],
}
}),
rootImport({
root: `${__dirname}/dist/js`,
useEntry: 'prepend',
extensions: '.js'
}),
json(),
globals(),
builtins(),
resolve()
]
}, 'umd'))
.on('error', function (e) {
console.log(e);
cb();
})
.pipe(gulp.dest('../../arvo/app/clock/js/'))
.on('end', cb);
});
gulp.task('tile-js-imports', function (cb) {
return gulp.src('dist/tile.js')
.pipe(rollup({
plugins: [
commonjs({
namedExports: {
'node_modules/react/index.js': ['Component'],
}
}),
rootImport({
root: `${__dirname}/dist/js`,
useEntry: 'prepend',
extensions: '.js'
}),
json(),
globals(),
builtins(),
resolve()
]
}, 'umd'))
.on('error', function (e) {
console.log(e);
cb();
})
.pipe(gulp.dest('../../arvo/app/clock/js/'))
.on('end', cb);
});
gulp.task('js-minify', function () {
return gulp.src('../../arvo/app/clock/js/index.js')
.pipe(minify())
.pipe(gulp.dest('../../arvo/app/clock/js/'));
});
gulp.task('tile-js-minify', function () {
return gulp.src('../../arvo/app/clock/js/tile.js')
.pipe(minify())
.pipe(gulp.dest('../../arvo/app/clock/js/'));
});
gulp.task('rename-tile-min', function() {
return gulp.src('../../arvo/app/clock/js/tile-min.js')
.pipe(rename('tile.js'))
.pipe(gulp.dest('../../arvo/app/clock/js/'));
});
gulp.task('clean-min', function(){
return del('../../arvo/app/clock/js/tile-min.js', {force: true})
})
gulp.task('urbit-copy', function () {
let ret = gulp.src('../../arvo/**/*');
urbitrc.URBIT_PIERS.forEach(function (pier) {
ret = ret.pipe(gulp.dest(pier));
});
return ret;
});
gulp.task('tile-js-bundle-dev', gulp.series('tile-jsx-transform', 'tile-js-imports'));
gulp.task('tile-js-bundle-prod',
gulp.series('tile-jsx-transform', 'tile-js-imports', 'tile-js-minify'));
gulp.task('bundle-prod',
gulp.series('tile-js-bundle-prod', 'rename-tile-min', 'clean-min', 'urbit-copy'));
gulp.task('default', gulp.series('tile-js-bundle-dev', 'urbit-copy'));
gulp.task('watch', gulp.series('default', function () {
gulp.watch('tile/**/*.js', gulp.parallel('tile-js-bundle-dev'));
gulp.watch('../../arvo/**/*', gulp.parallel('urbit-copy'));
}));

File diff suppressed because it is too large Load Diff

View File

@ -1,46 +0,0 @@
{
"name": "urbit-apps",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"devDependencies": {
"@joseph184/rollup-plugin-node-builtins": "^2.1.4",
"@sucrase/gulp-plugin": "^2.0.0",
"autoprefixer": "^9.6.1",
"cssnano": "^4.1.10",
"gulp": "^4.0.0",
"gulp-better-rollup": "^4.0.1",
"gulp-cssimport": "^7.0.0",
"gulp-minify": "^3.1.0",
"gulp-postcss": "^8.0.0",
"gulp-rename": "^1.4.0",
"rollup": "^1.6.0",
"rollup-plugin-commonjs": "^9.2.0",
"rollup-plugin-json": "^2.3.0",
"rollup-plugin-node-globals": "^1.4.0",
"rollup-plugin-node-resolve": "^3.4.0",
"rollup-plugin-replace": "^2.0.0",
"rollup-plugin-root-import": "^0.2.3",
"sucrase": "^3.8.0"
},
"dependencies": {
"classnames": "^2.2.6",
"del": "^5.1.0",
"lodash": "^4.17.11",
"moment": "^2.24.0",
"mousetrap": "^1.6.1",
"react": "^16.5.2",
"react-custom-scrollbars": "^4.2.1",
"react-dom": "^16.8.6",
"react-router-dom": "^5.0.0",
"suncalc": "^1.8.0"
},
"resolutions": {
"natives": "1.1.3"
}
}

View File

@ -1,63 +0,0 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
*.swp
.DS_Store
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# TypeScript v1 declaration files
typings/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
# next.js build output
.next

View File

@ -1,141 +0,0 @@
var gulp = require('gulp');
var cssimport = require('gulp-cssimport');
var rollup = require('gulp-better-rollup');
var cssnano = require('cssnano');
var autoprefixer = require('autoprefixer');
var postcss = require('gulp-postcss')
var sucrase = require('@sucrase/gulp-plugin');
var minify = require('gulp-minify');
var exec = require('child_process').exec;
var resolve = require('rollup-plugin-node-resolve');
var commonjs = require('rollup-plugin-commonjs');
var replace = require('rollup-plugin-replace');
var json = require('rollup-plugin-json');
var builtins = require('@joseph184/rollup-plugin-node-builtins');
var rootImport = require('rollup-plugin-root-import');
var globals = require('rollup-plugin-node-globals');
/***
Main config options
***/
var urbitrc = require('../urbitrc');
/***
End main config options
***/
gulp.task('css-bundle', function () {
let plugins = [
autoprefixer({ browsers: ['last 1 version'] }),
cssnano()
];
return gulp
.src('src/index.css')
.pipe(cssimport())
.pipe(postcss(plugins))
.pipe(gulp.dest('../../arvo/app/chat/css'));
});
gulp.task('tile-jsx-transform', function (cb) {
return gulp.src('tile/**/*.js')
.pipe(sucrase({
transforms: ['jsx']
}))
.pipe(gulp.dest('dist'));
});
gulp.task('js-imports', function (cb) {
return gulp.src('dist/index.js')
.pipe(rollup({
plugins: [
commonjs({
namedExports: {
'node_modules/react/index.js': ['Component'],
'node_modules/react-is/index.js': ['isValidElementType'],
}
}),
rootImport({
root: `${__dirname}/dist/js`,
useEntry: 'prepend',
extensions: '.js'
}),
json(),
globals(),
builtins(),
resolve()
]
}, 'umd'))
.on('error', function (e) {
console.log(e);
cb();
})
.pipe(gulp.dest('../../arvo/app/timer/js/'))
.on('end', cb);
});
gulp.task('tile-js-imports', function (cb) {
return gulp.src('dist/tile.js')
.pipe(rollup({
plugins: [
commonjs({
namedExports: {
'node_modules/react/index.js': ['Component'],
}
}),
rootImport({
root: `${__dirname}/dist/js`,
useEntry: 'prepend',
extensions: '.js'
}),
json(),
globals(),
builtins(),
resolve()
]
}, 'umd'))
.on('error', function (e) {
console.log(e);
cb();
})
.pipe(gulp.dest('../../arvo/app/timer/js/'))
.on('end', cb);
});
gulp.task('js-minify', function () {
return gulp.src('../../arvo/app/timer/js/index.js')
.pipe(minify())
.pipe(gulp.dest('../../arvo/app/timer/js/'));
});
gulp.task('tile-js-minify', function () {
return gulp.src('../../arvo/app/timer/js/tile.js')
.pipe(minify())
.pipe(gulp.dest('../../arvo/app/timer/js/'));
});
gulp.task('urbit-copy', function () {
let ret = gulp.src('../../arvo/**/*');
urbitrc.URBIT_PIERS.forEach(function (pier) {
ret = ret.pipe(gulp.dest(pier));
});
return ret;
});
gulp.task('tile-js-bundle-dev', gulp.series('tile-jsx-transform', 'tile-js-imports'));
gulp.task('tile-js-bundle-prod',
gulp.series('tile-jsx-transform', 'tile-js-imports', 'tile-js-minify'));
gulp.task('bundle-prod', gulp.series('tile-js-bundle-prod', 'urbit-copy'));
gulp.task('default', gulp.series('tile-js-bundle-dev', 'urbit-copy'));
gulp.task('watch', gulp.series('default', function () {
gulp.watch('tile/**/*.js', gulp.parallel('tile-js-bundle-dev'));
gulp.watch('tile/**/*.css', gulp.parallel('css-bundle'));
gulp.watch('../../arvo/**/*', gulp.parallel('urbit-copy'));
}));

File diff suppressed because it is too large Load Diff

View File

@ -1,39 +0,0 @@
{
"name": "urbit-apps",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"devDependencies": {
"@joseph184/rollup-plugin-node-builtins": "^2.1.4",
"@sucrase/gulp-plugin": "^2.0.0",
"autoprefixer": "^9.6.1",
"cssnano": "^4.1.10",
"gulp": "^4.0.0",
"gulp-better-rollup": "^4.0.1",
"gulp-cssimport": "^7.0.0",
"gulp-minify": "^3.1.0",
"gulp-postcss": "^8.0.0",
"rollup": "^1.6.0",
"rollup-plugin-commonjs": "^9.2.0",
"rollup-plugin-json": "^2.3.0",
"rollup-plugin-node-globals": "^1.4.0",
"rollup-plugin-node-resolve": "^3.4.0",
"rollup-plugin-replace": "^2.0.0",
"rollup-plugin-root-import": "^0.2.3",
"sucrase": "^3.8.0"
},
"dependencies": {
"classnames": "^2.2.6",
"lodash": "^4.17.11",
"moment": "^2.20.1",
"react": "^16.5.2"
},
"resolutions": {
"natives": "1.1.3"
}
}

View File

@ -1,40 +0,0 @@
class Api {
bind(app, path, success, fail, ship) {
window.urb.subscribe(ship, app, path,
(err) => {
fail(err, app, path, ship);
},
(event) => {
success({
data: event,
from: {
app,
ship,
path
}
});
},
(err) => {
fail(err, app, path, ship);
});
}
chess(data) {
this.action("chess", "chess-command", data);
}
action(appl, mark, data) {
return new Promise((resolve, reject) => {
window.urb.poke(ship, appl, mark, data,
(json) => {
resolve(json);
},
(err) => {
reject(err);
});
});
}
}
module.exports = new Api();

View File

@ -1,27 +0,0 @@
import React, { Component } from 'react';
export default class Flashing extends Component {
constructor(props) {
super(props);
this.alive = true;
this.state = { color: "black" };
}
//memory cleanup
componentWillUnmount() {
this.alive = false;
}
render() {
setTimeout(()=>{
if(this.alive) {
if(this.state.color == "black") { this.setState({color: "white"}) }
else if(this.state.color == "white") { this.setState({color: "black"}) }
}
},400);
return <div style={{ color: this.state.color }}>
{this.props.children}
</div>
}
}

View File

@ -1,37 +0,0 @@
import React, { Component } from 'react';
export default class VolumeIcon extends Component {
constructor(props) {
super(props);
this.toggleSound = this.toggleSound.bind(this);
}
toggleSound() {
this.props.parent.setState( {playSound: !this.props.parent.state.playSound} )
}
render() {
let on = this.props.parent.state.playSound;
return <div> <a onClick={()=> {
this.toggleSound()}}>
<img src=
{
on
?
"~timer/img/volume-high.png"
:
"~timer/img/volume-mute.png"
}
style={{
width:"30px",
height:"30px"
}}
></img>
</a>
</div>
}
}

View File

@ -1,284 +0,0 @@
import React, { Component } from 'react';
import classnames from 'classnames';
import api from './api';
import VolumeIcon from './components/volume-icon';
import Flashing from './components/flashing';
const timerLength = 60 * 20000;
const outerSize = 234; //size of tile itself
const innerSize = 200; //diameter of pomodoro
//not sure if this should be imported somehow or if copy and paste is the way to go
function daToDate(st) {
var dub = function(n) {
return parseInt(n) < 10 ? "0" + parseInt(n) : n.toString();
};
var da = st.split('..');
var bigEnd = da[0].split('.');
var lilEnd = da[1].split('.');
var ds = `${bigEnd[0].slice(1)}-${dub(bigEnd[1])}-${dub(bigEnd[2])}T${dub(lilEnd[0])}:${dub(lilEnd[1])}:${dub(lilEnd[2])}Z`;
return new Date(ds);
}
//polar to cartesian
var ptc = function(r, theta) {
return {
x: r * Math.cos(theta),
y: r * Math.sin(theta)
}
}
export default class TimerTile extends Component {
constructor(props) {
super(props);
this.animate = this.animate.bind(this);
this.state = this.getStateFromProps(props);
this.state.playSound = true;
}
componentDidMount() {
this.animate()
}
getStateFromProps(props) {
if(props.data.charAt(0) == "~"){
return {mode: "running", startTime: daToDate(props.data).getTime(), time: timerLength};
}
else if(props.data == "alarm" ) {
//api still delivers alarm events for cancelled timers, so make sure that it's actually time to fire the alarm before doing so
if( this.state && this.state.mode == "running" && this.state.time < 500 ) {
return {mode: "alarm", time:0};
}
else {
if(!this.state) { return {mode: "waiting", time: timerLength} }
else {
//no change
return {};
}
}
}
else {
return {mode: "waiting", time: timerLength};
}
}
componentWillReceiveProps(newProps) {
this.setState(this.getStateFromProps(newProps));
}
greenPart(ctx) {
//green thing on the tomato that rotates
const lightGreen = "#4da97a";
const darkGreen = "#386f55";
//define center of sprout based on time elapsed
var ratio = (timerLength-this.state.time) / timerLength; //ratio of elapsed time to total time
var easedRatio = ( -( Math.cos( Math.PI * ratio ) - 1 ) / 4) + (ratio/2);
//from here https://easings.net/en#easeInOutSine
//define scale based on time elapsed
var easedRatioCubic = ratio < 0.5 ?
4 * ratio * ratio * ratio :
1 - Math.pow( -2 * ratio + 2, 3 ) / 2;
var xCurve = outerSize/2-(.45*innerSize)*Math.sqrt(1-Math.pow((2*easedRatioCubic)-1,2));
var center = {x: xCurve, y: .95*innerSize*easedRatioCubic +1.4*(outerSize-innerSize)/2,};
var symmetricalEasedRatio = easedRatioCubic > .5 ? 1 - ((easedRatioCubic+ratio)/2) : ((easedRatioCubic+ratio)/2);
var scale = 1 + symmetricalEasedRatio*.6;
var rotation = ((ratio+easedRatio)/2) * -1 * Math.PI;
//generate star shape thing
var points = [];
for(var i = 0; i < 14; i++) {
var deg = i * (Math.PI / 7);
var p = ptc( i % 2 == 0 ? 15 : 40, deg);
points[i] = p;
}
ctx.save()
ctx.translate(center.x, center.y)
ctx.rotate(rotation);
ctx.scale(scale, scale/2);
ctx.fillStyle = lightGreen;
ctx.beginPath();
ctx.moveTo(points[0].x, points[0].y);
for(var i = 1; i < points.length; i++) {
ctx.lineTo(points[i].x,points[i].y);
}
ctx.closePath();
ctx.fill();
//draw the circle thing in the middle
ctx.fillStyle = darkGreen;
ctx.beginPath();
function drawEllipse(ctx, x, y, w, h) {
var kappa = .5522848,
ox = (w / 2) * kappa, // control point offset horizontal
oy = (h / 2) * kappa, // control point offset vertical
xe = x + w, // x-end
ye = y + h, // y-end
xm = x + w / 2, // x-middle
ym = y + h / 2; // y-middle
ctx.beginPath();
ctx.moveTo(x, ym);
ctx.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y);
ctx.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym);
ctx.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye);
ctx.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym);
//ctx.closePath(); // not used correctly, see comments (use to close off open path)
ctx.fill();
}
drawEllipse(ctx,-5,-5,10,10);
ctx.restore()
}
animate() {
//continuously animate
var c = document.getElementById("timer-canvas");
var ctx = c.getContext("2d");
//clear
ctx.clearRect(0, 0, c.width, c.height);
//draw body of pomodoro clock
ctx.fillStyle = "#ee5432";
ctx.beginPath();
ctx.arc(outerSize/2, outerSize/2, innerSize/2, 0, 2 * Math.PI);
ctx.fill();
//draw line separating top from bottom
ctx.strokeStyle = "black";
ctx.beginPath();
ctx.moveTo((outerSize-innerSize)/2, outerSize/2);
ctx.lineTo(innerSize+(outerSize-innerSize)/2,outerSize/2);
ctx.stroke();
this.greenPart(ctx);
if(this.state.mode == "running") {
var time = -1 * ((new Date()).getTime() - this.state.startTime);
//javascript time can be ahead of the urbit alarm, so we dont want to show negative nubmers
if(time < 0) { time = 0; }
this.setState({time: time})
}
window.requestAnimationFrame(this.animate)
}
formatTime(time) {
var seconds = Math.ceil(time / 1000); //rounding up seems to result in better UX
var minutes = Math.floor(seconds / 60);
//convert to string
seconds += "";
minutes += "";
if(seconds.length == 1) { seconds = "0" + seconds }
if(minutes.length == 1) { minutes = "0" + minutes }
return minutes + ":" + seconds;
}
startTimer() {
api.action('timer', 'json', 'start');
}
stopTimer() {
api.action('timer', 'json', 'stop');
}
renderWrapper(child) {
return (
<div style={{
width: outerSize,
height: outerSize,
background: '#1a1a1a'
}}>
{child}
</div>
);
}
render() {
var interaction;
var interactionStyle = "link underline black hover-white";
if(this.state.mode == "running") {
interaction = <a className={interactionStyle} onClick={this.stopTimer}>Stop</a>;
}
else if(this.state.mode == "alarm") {
interaction = <a className={interactionStyle} onClick={this.stopTimer}>Shhh</a>;
}
else {
interaction = <a className={interactionStyle} onClick={this.startTimer}>Start</a>;
}
return this.renderWrapper((
<div style={{ position: "relative",
fontFamily: "-apple-system,BlinkMacSystemFont,avenir next,avenir,helvetica neue,helvetica,ubuntu,roboto,noto,segoe ui,arial,sans-serif"
}}>
<canvas id="timer-canvas" width={outerSize} height={outerSize}></canvas>
<div id="timer-display" style={{
width: "100%",
textAlign: "center",
position: "absolute",
top: "120px",
left: "0px",
fontSize:"28px",
fontWeight:"300"
}}>
{this.state.mode == "running" ? this.formatTime(this.state.time) :
this.state.mode == "alarm" ?
<div>
<Flashing>00:00</Flashing>
{
this.state.playSound
?
<audio src="http://maxwellsfoley.com/ding.mp3" loop={true} autoPlay/>
:
null
}
</div>
:
'00:00'}
</div>
<div id="timer-interact" style={{
width: "100%",
textAlign: "center",
position: "absolute",
top: "170px",
left: "0px",
fontSize:"16px"
}}>
{ interaction }
</div>
<div id="volume-container" style={{
position: "absolute",
top: "200px",
left: "5px"
}}>
<VolumeIcon parent={this}/>
</div>
</div>
));
}
}
window.timerTile = TimerTile;

View File

@ -1,155 +0,0 @@
var gulp = require('gulp');
var cssimport = require('gulp-cssimport');
var rollup = require('gulp-better-rollup');
var cssnano = require('cssnano');
var autoprefixer = require('autoprefixer');
var postcss = require('gulp-postcss')
var sucrase = require('@sucrase/gulp-plugin');
var minify = require('gulp-minify');
var exec = require('child_process').exec;
var rename = require('gulp-rename');
var del = require('del');
var resolve = require('rollup-plugin-node-resolve');
var commonjs = require('rollup-plugin-commonjs');
var replace = require('rollup-plugin-replace');
var json = require('rollup-plugin-json');
var builtins = require('@joseph184/rollup-plugin-node-builtins');
var rootImport = require('rollup-plugin-root-import');
var globals = require('rollup-plugin-node-globals');
/***
Main config options
***/
var urbitrc = require('../urbitrc');
/***
End main config options
***/
gulp.task('jsx-transform', function(cb) {
return gulp.src('src/**/*.js')
.pipe(sucrase({
transforms: ['jsx']
}))
.pipe(gulp.dest('dist'));
});
gulp.task('tile-jsx-transform', function(cb) {
return gulp.src('tile/**/*.js')
.pipe(sucrase({
transforms: ['jsx']
}))
.pipe(gulp.dest('dist'));
});
gulp.task('js-imports', function (cb) {
return gulp.src('dist/index.js')
.pipe(rollup({
plugins: [
commonjs({
namedExports: {
'node_modules/react/index.js': ['Component'],
'node_modules/react-is/index.js': ['isValidElementType'],
}
}),
rootImport({
root: `${__dirname}/dist/js`,
useEntry: 'prepend',
extensions: '.js'
}),
json(),
globals(),
builtins(),
resolve()
]
}, 'umd'))
.on('error', function (e) {
console.log(e);
cb();
})
.pipe(gulp.dest('../../arvo/app/weather/js/'))
.on('end', cb);
});
gulp.task('tile-js-imports', function(cb) {
return gulp.src('dist/tile.js')
.pipe(rollup({
plugins: [
commonjs({
namedExports: {
'node_modules/react/index.js': [ 'Component' ],
}
}),
rootImport({
root: `${__dirname}/dist/js`,
useEntry: 'prepend',
extensions: '.js'
}),
json(),
globals(),
builtins(),
resolve()
]
}, 'umd'))
.on('error', function(e){
console.log(e);
cb();
})
.pipe(gulp.dest('../../arvo/app/weather/js/'))
.on('end', cb);
});
gulp.task('js-minify', function () {
return gulp.src('../../arvo/app/weather/js/index.js')
.pipe(minify())
.pipe(gulp.dest('../../arvo/app/weather/js/'));
});
gulp.task('tile-js-minify', function () {
return gulp.src('../../arvo/app/weather/js/tile.js')
.pipe(minify())
.pipe(gulp.dest('../../arvo/app/weather/js/'));
});
gulp.task('rename-index-min', function() {
return gulp.src('../../arvo/app/weather/js/index-min.js')
.pipe(rename('index.js'))
.pipe(gulp.dest('../../arvo/weather/chat/js/'));
});
gulp.task('rename-tile-min', function() {
return gulp.src('../../arvo/app/weather/js/tile-min.js')
.pipe(rename('tile.js'))
.pipe(gulp.dest('../../arvo/app/weather/js/'));
});
gulp.task('clean-min', function(){
return del('../../arvo/app/weather/js/tile-min.js', {force: true})
})
gulp.task('urbit-copy', function () {
let ret = gulp.src('../../arvo/**/*');
urbitrc.URBIT_PIERS.forEach(function(pier) {
ret = ret.pipe(gulp.dest(pier));
});
return ret;
});
gulp.task('tile-js-bundle-dev', gulp.series('tile-jsx-transform', 'tile-js-imports'));
gulp.task('tile-js-bundle-prod',
gulp.series('tile-jsx-transform', 'tile-js-imports', 'tile-js-minify'));
gulp.task('bundle-prod',
gulp.series('tile-js-bundle-prod', 'rename-tile-min', 'clean-min', 'urbit-copy'));
gulp.task('default', gulp.series('tile-js-bundle-dev', 'urbit-copy'));
gulp.task('watch', gulp.series('default', function () {
gulp.watch('tile/**/*.js', gulp.parallel('tile-js-bundle-dev'));
gulp.watch('../../arvo/**/*', gulp.parallel('urbit-copy'));
}));

File diff suppressed because it is too large Load Diff

View File

@ -1,45 +0,0 @@
{
"name": "urbit-apps",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"devDependencies": {
"@joseph184/rollup-plugin-node-builtins": "^2.1.4",
"@sucrase/gulp-plugin": "^2.0.0",
"autoprefixer": "^9.6.1",
"cssnano": "^4.1.10",
"gulp": "^4.0.0",
"gulp-better-rollup": "^4.0.1",
"gulp-cssimport": "^7.0.0",
"gulp-minify": "^3.1.0",
"gulp-postcss": "^8.0.0",
"gulp-rename": "^1.4.0",
"rollup": "^1.6.0",
"rollup-plugin-commonjs": "^9.2.0",
"rollup-plugin-json": "^2.3.0",
"rollup-plugin-node-globals": "^1.4.0",
"rollup-plugin-node-resolve": "^3.4.0",
"rollup-plugin-replace": "^2.0.0",
"rollup-plugin-root-import": "^0.2.3",
"sucrase": "^3.8.0"
},
"dependencies": {
"classnames": "^2.2.6",
"del": "^5.1.0",
"lodash": "^4.17.11",
"moment": "^2.20.1",
"mousetrap": "^1.6.1",
"react": "^16.5.2",
"react-custom-scrollbars": "^4.2.1",
"react-dom": "^16.8.6",
"react-router-dom": "^5.0.0"
},
"resolutions": {
"natives": "1.1.3"
}
}

View File

@ -5,10 +5,49 @@ export default class LaunchReducer {
const data = _.get(json, 'launch-update', false);
if (data) {
this.log(data, state);
this.initial(data, state);
this.changeFirstTime(data, state);
this.changeOrder(data, state);
this.changeFirstTime(data, state);
this.changeIsShown(data, state);
}
}
log(json, state) {
console.log(json);
}
initial(json, state) {
const data = _.get(json, 'initial', false);
if (data) {
state.launch = data;
}
}
changeFirstTime(json, state) {
const data = _.get(json, 'changeFirstTime', false);
if (data) {
state.launch.firstTime = data;
}
}
changeOrder(json, state) {
const data = _.get(json, 'changeOrder', false);
if (data) {
state.launch.tileOrdering = data;
}
}
changeIsShown(json, state) {
const data = _.get(json, 'changeIsShown', false);
console.log(json, data);
if (data) {
let tile = state.launch.tiles[data.name];
console.log(tile);
if (tile) {
tile.isShown = data.isShown;
}
}
}
}

View File

@ -7,6 +7,16 @@ export default class LaunchStore extends BaseStore {
this.launchReducer = new LaunchReducer();
}
initialState() {
return {
launch: {
firstTime: false,
tileOrdering: [],
tiles: {}
}
};
}
reduce(data, state) {
this.launchReducer.reduce(data, state);
}