Added Max's timer and clock apps

This commit is contained in:
Logan Allen 2019-05-28 16:19:07 -07:00
parent bcfface83e
commit cc35305388
29 changed files with 20168 additions and 104 deletions

View File

@ -51,10 +51,6 @@ class UrbitApi {
});
}
/*
Special actions
*/
permit(cir, aud, message) {
this.hall({
permit: {
@ -103,22 +99,6 @@ class UrbitApi {
});
}
message(aud, words) {
let msg = {
aud,
ses: [{
lin: {
msg: words,
pat: false
}
}]
};
this.hall({
phrase: msg
});
}
source(nom, sub) {
this.hall({
source: {
@ -146,37 +126,6 @@ class UrbitApi {
}
})
}
create(nom, priv) {
this.hall({
create: {
nom: nom,
des: "chatroom",
sec: priv ? "village" : "channel"
}
});
}
ire(aud, uid, msg) {
let message = {
aud: aud,
ses: [{
ire: {
top: uid,
sep: {
lin: {
msg: msg,
pat: false
}
}
}
}]
}
this.hall({
phrase: message
})
}
}
export let api = new UrbitApi();

View File

@ -44,7 +44,7 @@
|= old=(unit state)
^- (quip move _this)
?~ old
=/ inboxpat /circle/inbox/config/group
=/ inboxpat /circle/inbox/config/peers
=/ circlespat /circles/[(scot %p our.bol)]
=/ inboxi/poke
:- %hall-action

View File

@ -17,6 +17,10 @@
return module = { exports: {} }, fn(module, module.exports), module.exports;
}
function getCjsExportFromNamespace (n) {
return n && n.default || n;
}
/*
object-assign
(c) Sindre Sorhus
@ -47458,6 +47462,8 @@
isBuffer: isBuffer
});
var require$$0 = getCjsExportFromNamespace(bufferEs6);
var bn = createCommonjsModule(function (module) {
(function (module, exports) {
@ -47510,7 +47516,7 @@
var Buffer;
try {
Buffer = bufferEs6.Buffer;
Buffer = require$$0.Buffer;
} catch (e) {
}
@ -51744,10 +51750,6 @@ lyrtesmudnytbyrsenwegfyrmurtelreptegpecnelnevfes\
});
}
/*
Special actions
*/
permit(cir, aud, message) {
this.hall({
permit: {
@ -51796,22 +51798,6 @@ lyrtesmudnytbyrsenwegfyrmurtelreptegpecnelnevfes\
});
}
message(aud, words) {
let msg = {
aud,
ses: [{
lin: {
msg: words,
pat: false
}
}]
};
this.hall({
phrase: msg
});
}
source(nom, sub) {
this.hall({
source: {
@ -51839,37 +51825,6 @@ lyrtesmudnytbyrsenwegfyrmurtelreptegpecnelnevfes\
}
});
}
create(nom, priv) {
this.hall({
create: {
nom: nom,
des: "chatroom",
sec: priv ? "village" : "channel"
}
});
}
ire(aud, uid, msg) {
let message = {
aud: aud,
ses: [{
ire: {
top: uid,
sep: {
lin: {
msg: msg,
pat: false
}
}
}
}]
};
this.hall({
phrase: message
});
}
}
let api = new UrbitApi();

63
apps/clock/.gitignore vendored Normal file
View File

@ -0,0 +1,63 @@
# 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

21
apps/clock/LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019 urbit
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

10
apps/clock/README.md Normal file
View File

@ -0,0 +1,10 @@
# Weather tile for Urbit
To install this on your Urbit planet:
1. In your Urbit's Dojo, run |mount %
2. Write in the filepath to your Urbit's pier in the urbitrc-sample file in this repository, then copy it to .urbitrc in this directory.
3. Run `npm install` in terminal in this directory.
4. Run `gulp default` in terminal in this directory.
5. Run |start %weather in your Urbit's Dojo.
To see it, navigate to your Urbit's url and add /~home to the URL path.

123
apps/clock/gulpfile.js Normal file
View File

@ -0,0 +1,123 @@
var gulp = require('gulp');
var cssimport = require('gulp-cssimport');
var cssnano = require('gulp-cssnano');
var rollup = require('gulp-better-rollup');
var sucrase = require('@sucrase/gulp-plugin');
var minify = require('gulp-minify');
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('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('./urbit/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('./urbit/app/clock/js/'))
.on('end', cb);
});
gulp.task('js-minify', function () {
return gulp.src('./urbit/app/clock/js/index.js')
.pipe(minify())
.pipe(gulp.dest('./urbit/app/clock/js/'));
});
gulp.task('urbit-copy', function () {
let ret = gulp.src('urbit/**/*');
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('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('urbit/**/*', gulp.parallel('urbit-copy'));
}));

7035
apps/clock/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

37
apps/clock/package.json Normal file
View File

@ -0,0 +1,37 @@
{
"name": "urbit-apps",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"devDependencies": {
"@sucrase/gulp-plugin": "^2.0.0",
"gulp": "^4.0.0",
"rollup": "^1.6.0",
"gulp-better-rollup": "^4.0.1",
"gulp-cssimport": "^6.0.0",
"gulp-cssnano": "^2.1.2",
"gulp-minify": "^3.1.0",
"rollup-plugin-commonjs": "^9.2.0",
"rollup-plugin-json": "^2.3.0",
"rollup-plugin-node-builtins": "^2.1.2",
"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",
"react": "^16.5.2",
"react-dom": "^16.5.2",
"react-router-dom": "^5.0.0"
},
"resolutions": {
"natives": "1.1.3"
}
}

169
apps/clock/tile/tile.js Normal file
View File

@ -0,0 +1,169 @@
import React, { Component } from 'react';
import classnames from 'classnames';
const outerSize = 234; //tile size
const innerSize = 218; //clock size
//polar to cartesian
var ptc = function(r, theta) {
return {
x: r * Math.cos(theta),
y: r * Math.sin(theta)
}
}
class Clock extends Component {
constructor(props) {
super(props);
this.animate = this.animate.bind(this);
this.hourHand = this.hourHand.bind(this);
this.minuteHand = this.minuteHand.bind(this);
this.secondHand = this.secondHand.bind(this);
}
componentDidMount() {
this.animate()
}
dodecagon(ctx) {
var points = [];
//first create array of twelve points
for(var i = 0; i < 12; i++) {
var deg = i * (Math.PI / 6);
var p = ptc(innerSize/2, deg);
p.x += innerSize/2;
p.y += innerSize/2;
points[i] = p;
}
ctx.fillStyle = "white";
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();
}
hourHand(ctx, time) {
//get number of minutes in the day so far, this is so hour hand moves gradually
// rather than in large ticks
var mins = time.getMinutes() + (60 * time.getHours());
//draw the circle thing in the middle
ctx.fillStyle = "black";
ctx.beginPath();
ctx.arc(innerSize/2, innerSize/2, 5, 0, 2 * Math.PI);
ctx.fill();
//draw the actual hour hand
ctx.strokeStyle = "black";
ctx.beginPath();
ctx.moveTo(innerSize/2, innerSize/2);
var angle = (Math.PI/-2.0) + ((2*Math.PI/(12*60)) * mins);
var p = ptc(innerSize*.22, angle);
p.x += innerSize/2;
p.y += innerSize/2;
ctx.lineTo(p.x,p.y);
ctx.stroke();
}
minuteHand(ctx, time) {
//number of seconds in the hour so far
var secs = time.getSeconds() + (60 * time.getMinutes());
ctx.strokeStyle = "black";
ctx.beginPath();
ctx.moveTo(innerSize/2, innerSize/2);
var angle = (Math.PI/-2.0) + ((2*Math.PI/(60*60)) * secs);
var p = ptc(innerSize*.35, angle);
p.x += innerSize/2;
p.y += innerSize/2;
ctx.lineTo(p.x,p.y);
ctx.stroke();
}
secondHand(ctx, time) {
//get number of ms in minute so far
var secs = time.getSeconds();
let middle = {x: innerSize/2, y: innerSize*.75};
//draw the circle thing in the middle
ctx.fillStyle = "red";
ctx.beginPath();
ctx.arc(middle.x, middle.y, 5, 0, 2 * Math.PI);
ctx.fill();
//draw the actual second hand
ctx.strokeStyle = "red";
ctx.beginPath();
var angle = (Math.PI/-2.0) + ((2*Math.PI/(60)) * secs);
var p = ptc(30, angle);
var p2 = ptc(-10, angle ); //starting point is a little bit off center in the opposite direction
p.x += middle.x;
p.y += middle.y;
p2.x += middle.x;
p2.y += middle.y
ctx.moveTo(p2.x, p2.y);
ctx.lineTo(p.x,p.y);
ctx.stroke();
}
animate() {
var time = new Date();
//continuously animate
var c = document.getElementById("clock-canvas");
var ctx = c.getContext("2d");
ctx.clearRect(0, 0, c.width, c.height);
ctx.save();
ctx.translate(0.5, 0.5); //forces antialias thus smoothing out jagged lines
//draw the clock itself
this.dodecagon(ctx);
//draw the hands
this.secondHand(ctx, time);
this.hourHand(ctx, time);
this.minuteHand(ctx, time);
ctx.restore();
window.requestAnimationFrame(this.animate)
}
render() {
return <div>
<canvas id="clock-canvas" width={innerSize} height={innerSize}></canvas>
</div>
}
}
export default class ClockTile extends Component {
constructor(props) {
super(props);
}
renderWrapper(child) {
return (
<div className="pa2 bg-dark-gray" style={{ width: outerSize, height: outerSize }}>
{child}
</div>
);
}
render() {
let data = !!this.props.data ? this.props.data : {};
return this.renderWrapper((
<Clock/>
));
}
}
window.clockTile = ClockTile;

View File

@ -0,0 +1,75 @@
/+ *server
/= tile-js
/^ octs
/; as-octs:mimes:html
/: /===/app/clock/js/tile /js/
=, format
::
|%
:: +move: output effect
::
+$ move [bone card]
:: +card: output effect payload
::
+$ poke
$% [%noun [@tas path]]
==
::
+$ card
$% [%poke wire dock poke]
[%http-response =http-event:http]
[%connect wire binding:http-server term]
[%diff %json json]
==
::
--
::
|_ [bol=bowl:gall ~]
::
++ this .
::
++ bound
|= [wir=wire success=? binding=binding:http-server]
^- (quip move _this)
[~ this]
::
++ prep
|= old=(unit ~)
^- (quip move _this)
=/ lismov/(list move) %+ weld
`(list move)`[ost.bol %connect / [~ /'~clock'] %clock]~
`(list move)`[ost.bol %poke /clock [our.bol %launch] [%noun [%clock /tile]]]~
:- lismov
this
::
++ peer-tile
|= pax=path
^- (quip move _this)
[[ost.bol %diff %json *json]~ this]
::
++ send-tile-diff
|= jon=json
^- (list move)
%+ turn (prey:pubsub:userlib /tile bol)
|= [=bone ^]
[bone %diff %json jon]
::
++ poke-handle-http-request
%- (require-authorization:app ost.bol move this)
|= =inbound-request:http-server
^- (quip move _this)
=+ request-line=(parse-request-line url.request.inbound-request)
=+ back-path=(flop site.request-line)
=/ name=@t
=+ back-path=(flop site.request-line)
?~ back-path
''
i.back-path
::
?~ back-path
[[ost.bol %http-response not-found:app]~ this]
?: =(name 'tile')
[[ost.bol %http-response (js-response:app tile-js)]~ this]
[[ost.bol %http-response not-found:app]~ this]
::
--

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,5 @@
module.exports = {
URBIT_PIERS: [
"/Users/logan/Dev/light-urbit/build/zod-chess-9/home",
]
};

63
apps/timer/.gitignore vendored Normal file
View File

@ -0,0 +1,63 @@
# 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

21
apps/timer/LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019 urbit
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

10
apps/timer/README.md Normal file
View File

@ -0,0 +1,10 @@
# Weather tile for Urbit
To install this on your Urbit planet:
1. In your Urbit's Dojo, run |mount %
2. Write in the filepath to your Urbit's pier in the urbitrc-sample file in this repository, then copy it to .urbitrc in this directory.
3. Run `npm install` in terminal in this directory.
4. Run `gulp default` in terminal in this directory.
5. Run |start %weather in your Urbit's Dojo.
To see it, navigate to your Urbit's url and add /~home to the URL path.

125
apps/timer/gulpfile.js Normal file
View File

@ -0,0 +1,125 @@
var gulp = require('gulp');
var cssimport = require('gulp-cssimport');
var cssnano = require('gulp-cssnano');
var rollup = require('gulp-better-rollup');
var sucrase = require('@sucrase/gulp-plugin');
var minify = require('gulp-minify');
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('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() {
return gulp
.src('tile/index.css')
.pipe(cssimport())
.pipe(cssnano())
.pipe(gulp.dest('./urbit/app/home/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('./urbit/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('./urbit/app/timer/js/'))
.on('end', cb);
});
gulp.task('js-minify', function () {
return gulp.src('./urbit/app/timer/js/index.js')
.pipe(minify())
.pipe(gulp.dest('./urbit/app/timer/js/'));
});
gulp.task('urbit-copy', function () {
let ret = gulp.src('urbit/**/*');
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('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('urbit/**/*', gulp.parallel('urbit-copy'));
}));

6887
apps/timer/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

38
apps/timer/package.json Normal file
View File

@ -0,0 +1,38 @@
{
"name": "urbit-apps",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"devDependencies": {
"@sucrase/gulp-plugin": "^2.0.0",
"gulp": "^4.0.2",
"gulp-better-rollup": "^4.0.1",
"gulp-cssimport": "^6.0.0",
"gulp-cssnano": "^2.1.2",
"gulp-minify": "^3.1.0",
"rollup": "^1.6.0",
"rollup-plugin-commonjs": "^9.2.0",
"rollup-plugin-json": "^2.3.0",
"rollup-plugin-node-builtins": "^2.1.2",
"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",
"react": "^16.5.2",
"react-dom": "^16.5.2",
"react-router-dom": "^5.0.0",
"styled-components": "^4.2.0"
},
"resolutions": {
"natives": "1.1.3"
}
}

40
apps/timer/tile/api.js Normal file
View File

@ -0,0 +1,40 @@
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

@ -0,0 +1,27 @@
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

@ -0,0 +1,37 @@
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>
}
}

282
apps/timer/tile/tile.js Normal file
View File

@ -0,0 +1,282 @@
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 = 10000;
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 className="bg-dark-gray" style={{ width: outerSize, height: outerSize }}>
{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

@ -0,0 +1,124 @@
/+ *server
/= tile-js
/^ octs
/; as-octs:mimes:html
/: /===/app/timer/js/tile /js/
/= timer-png
/^ (map knot @)
/: /===/app/timer/img /_ /png/
=, format
::
|%
:: +move: output effect
::
+$ move [bone card]
:: +card: output effect payload
::
+$ poke
$% [%noun [@tas path]]
==
::
+$ card
$% [%poke wire dock poke]
[%http-response =http-event:http]
[%connect wire binding:http-server term]
[%diff %json json]
[%wait wire @da]
[%rest wire @da]
==
::
--
::
|_ [bol=bowl:gall tim=@da]
::
++ this .
::
++ bound
|= [wir=wire success=? binding=binding:http-server]
^- (quip move _this)
[~ this]
::
++ prep
|= old=(unit tim=@da)
^- (quip move _this)
~& 'prep timer'
=/ lismov/(list move) %+ weld
`(list move)`[ost.bol %connect / [~ /'~timer'] %timer]~
`(list move)`[ost.bol %poke /timer [our.bol %launch] [%noun [%timer /tile]]]~
:- lismov
?~ old
this
%= this
tim tim.u.old
==
::
++ peer-tile
|= pax=path
^- (quip move _this)
?: =(tim *@da)
[[ost.bol %diff %json [%s '']]~ this]
[[ost.bol %diff %json [%s (scot %da tim)]]~ this]
::
++ send-tile-diff
|= jon=json
^- (list move)
%+ turn (prey:pubsub:userlib /tile bol)
|= [=bone ^]
[bone %diff %json jon]
::
++ poke-json
|= jon=json
^- (quip move _this)
~& jon
?. ?=(%s -.jon)
[~ this]
=/ str/@t +.jon
?: =(str 'start')
=/ data/@da (add now.bol ~s10)
:- %+ weld
`(list move)`(send-tile-diff [%s (scot %da data)])
`(list move)`[ost.bol %wait /timer data]~
this(tim data)
?: =(str 'stop')
:- %+ weld
`(list move)`(send-tile-diff [%s ''])
`(list move)`[ost.bol %rest /timer tim]~
this(tim *@da)
[~ this]
::
++ poke-handle-http-request
%- (require-authorization:app ost.bol move this)
|= =inbound-request:http-server
^- (quip move _this)
=+ request-line=(parse-request-line url.request.inbound-request)
=+ back-path=(flop site.request-line)
=/ name=@t
=+ back-path=(flop site.request-line)
?~ back-path
''
i.back-path
::
~& site.request-line
?+ site.request-line
[[ost.bol %http-response not-found:app]~ this]
::
:: tile
::
[%'~timer' %tile ~]
[[ost.bol %http-response (js-response:app tile-js)]~ this]
::
:: images
::
[%'~timer' %img *]
=/ img (as-octs:mimes:html (~(got by timer-png) `@ta`name))
:_ this
[ost.bol %http-response (png-response:app img)]~
==
::
++ wake
|= [wir=wire err=(unit tang)]
^- (quip move _this)
:- (send-tile-diff [%s 'alarm'])
this(tim *@da)
::
--

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,5 @@
module.exports = {
URBIT_PIERS: [
"/Users/logan/Dev/light-urbit/build/zod-chess-9/home",
]
};