Merge branch 'apps' of https://github.com/urbit/urbit into work

This commit is contained in:
Philip C Monk 2015-08-21 20:02:51 -04:00
commit a265e34e64
13 changed files with 522 additions and 459 deletions

View File

@ -57,10 +57,10 @@
++ task
%- ot :~
audience/audi
id/id date-created/di
version/ni date-modified/di
id/id 'date_created'^di
version/ni 'date_modified'^di
owner/ship status/(ci (soft status) so)
tags/(as so) date-due/(mu di)
tags/(as so) 'date_due'^(mu di)
done/(mu di) title/so
description/so discussion/(ar (ot date/di ship/ship body/so ~))
==

View File

@ -20,16 +20,16 @@
owner/(jope owner)
title/[%s title]
status/[%s status]
version/[%s (scot %ud version)]
version/(jone version)
claiming/[%b claiming]
=< audience/[%a (turn (~(tap in audience)) .)]
|=(a=station:talk [%s (crip "{<p.a>}/{(trip q.a)}")])
date-created/(jode date-created)
date-modified/(jode date-modified)
'date_created'^(jode date-created)
'date_modified'^(jode date-modified)
description/[%s description]
=< discussion/[%a (turn discussion .)]
|=(comment (jobe date/(jode date) ship/(jope ship) body/[%s body] ~))
date-due/?~(date-due ~ (jode u.date-due))
'date_due'^?~(date-due ~ (jode u.date-due))
done/?~(done ~ (jode u.done))
==
==

View File

@ -105,6 +105,13 @@ h1.leader:after {
content: "—";
margin-left: 0.6rem;
}
.ctrl .sort label,
.ctrl .filter label,
.item .status {
text-transform: uppercase;
font-size: 0.7rem;
letter-spacing: 0.07rem;
}
.ctrl {
margin-top: 2rem;
}
@ -114,13 +121,6 @@ h1.leader:after {
.ctrl .sorts {
margin-left: -0.3rem;
}
.ctrl .sort label,
.ctrl .filter label {
text-transform: uppercase;
font-size: 0.7rem;
line-height: 2rem;
letter-spacing: 0.07rem;
}
.ctrl .filter {
line-height: 2rem;
margin-right: 1rem;
@ -131,6 +131,10 @@ h1.leader:after {
border: 0;
cursor: pointer;
}
.ctrl .sort label,
.ctrl .filter label {
line-height: 2rem;
}
.ctrl .sort.s-1,
.ctrl .sort.s--1 {
font-weight: 500;
@ -167,6 +171,13 @@ h1.leader:after {
transform: rotate(90deg);
transition: transform 200ms linear;
}
.item .header {
display: block;
width: 100%;
height: 1rem;
overflow: hidden;
white-space: nowrap;
}
.item .sort,
.item .title,
.item .date,
@ -196,18 +207,20 @@ h1.leader:after {
letter-spacing: 0.07rem;
}
.item .owner {
width: 7rem;
margin-left: 2rem;
max-width: 7rem;
margin: 0 1rem 0 2rem;
}
.item .release {
.item .label {
font-style: italic;
color: #555;
}
.item:hover .action-true .label,
.item .action {
display: none;
}
.item:hover .release {
.item:hover .action-true .action {
display: inline-block;
}
.item:hover .mine {
display: none;
}
.item .audience {
margin-left: 2.2rem;
width: 24rem;

View File

@ -27,6 +27,13 @@ h1.leader:after
content ""
margin-left .6rem
.ctrl .sort label
.ctrl .filter label
.item .status
text-transform uppercase
font-size .7rem
letter-spacing .07rem
.ctrl
margin-top 2rem
@ -35,13 +42,6 @@ h1.leader:after
.sorts
margin-left -.3rem
.sort label
.filter label
text-transform uppercase
font-size .7rem
line-height 2rem
letter-spacing .07rem
.filter
line-height 2rem
@ -52,6 +52,10 @@ h1.leader:after
outline none
border 0
cursor pointer
.sort label
.filter label
line-height 2rem
.sort.s-1
.sort.s--1
@ -89,6 +93,13 @@ h1.leader:after
&.expand .expand
transform rotate(90deg)
transition transform 200ms linear
.header
display block
width 100%
height 1rem
overflow hidden
white-space nowrap
.sort
.title
@ -119,18 +130,19 @@ h1.leader:after
letter-spacing .07rem
.owner
width 7rem
margin-left 2rem
max-width 7rem
margin 0 1rem 0 2rem
.release
.label
font-style italic
color #555
&:hover .action-true .label
.action
display none
&:hover .release
&:hover .action-true .action
display inline-block
&:hover .mine
display none
.audience
margin-left 2.2rem

View File

@ -2,64 +2,60 @@ Dispatcher = require '../dispatcher/Dispatcher.coffee'
Persistence = require '../persistence/Persistence.coffee'
module.exports =
newItem: (index,list) ->
newItem: (index,_item={}) ->
item =
id:window.util.uuid32()
version:0
"date-created":Date.now()
"date-modified":Date.now()
"date-due":null
done:null
owner:window.urb.ship
status:'announced'
tags:[]
title:''
description:''
discussion:[]
audience:[window.util.talk.mainStationPath window.urb.ship]
Persistence.put "new":item
id: window.util.uuid32()
version: 0
owner: window.urb.ship
date_created: Date.now()
date_modified: Date.now()
date_due: _item.date_due ? null
done: _item.done ? null
status: _item.status ? 'announced'
tags: _item.tags ? []
title: _item.title ? ''
description: _item.description ? ''
discussion: _item.discussion ? []
audience: _item.audience ?
[window.util.talk.mainStationPath window.urb.ship]
Persistence.put new:item
Dispatcher.handleViewAction {type:'newItem', index, item}
setItem: (id,version,key,val) ->
setItem: ({id,version},key,val) ->
set = {}
key = key.split('_').join '-'
set[key] = val
version += 1
Persistence.put old:{id,version,dif:{set}}
addComment: (id,version,val) ->
ownItem: ({id,version},own) ->
o = {}
o[own] = null
version += 1
Persistence.put old:{id,version,dif:own:o}
addItem: ({id,version},val) ->
version += 1
Persistence.put old:{id,version,dif:add:comment:val}
setFilter: (key,val) ->
Dispatcher.handleViewAction
type:'setFilter'
key:key
val:val
setSort: (key,val) ->
Dispatcher.handleViewAction
type:'setSort'
key:key
val:val
swapItems: (to,from) ->
Dispatcher.handleViewAction
type:'swapItem'
from:from
to:to
removeItem: (index,id) ->
Persistence.put old:{id,dif:set:done:true}
Dispatcher.handleViewAction
type:'removeItem'
index:index
setFilter: (key,val) -> Dispatcher.handleViewAction {type:'setFilter', key,val}
setSort: (key,val) -> Dispatcher.handleViewAction {type:'setSort',key,val}
moveItem: (list,to,from) ->
sort = _.clone list
sort.splice to, 0, sort.splice(from,1)[0]
Persistence.put {sort}
Dispatcher.handleViewAction {list:sort,to,from,type:'moveItems'}
addItem: (index,item) ->
Dispatcher.handleViewAction
type:'addItem'
index:index
item:item
Dispatcher.handleViewAction {type:'addItem',index,item}
removeItem: ({id,version},index) ->
Persistence.put old:{id,version,dif:set:done:true}
Dispatcher.handleViewAction {type:'removeItem',index}
listenList: (type)->
Persistence.subscribe type, (err,d)->
Persistence.subscribe type, (err,d)->
if d?
{sort,tasks} = d.data
Dispatcher.handleServerAction {type:"getData",sort,tasks}

View File

@ -1,6 +1,6 @@
recl = React.createClass
rece = React.createElement
[div,h1,label] = [React.DOM.div,React.DOM.h1,React.DOM.label]
{div,h1,label} = React.DOM
module.exports = recl
_onKeyDown: (e) ->

View File

@ -1,19 +1,22 @@
recl = React.createClass
[div,textarea] = [React.DOM.div,React.DOM.textarea]
{div,textarea} = React.DOM
WorkActions = require '../actions/WorkActions.coffee'
module.exports = recl
_dragStart: (e) ->
onDragStart: (e) ->
unless @props.draggable
e.preventDefault()
return
$t = $(e.target)
@dragged = $t.closest('.item')
e.dataTransfer.effectAllowed = 'move'
e.dataTransfer.setData 'text/html',e.currentTarget
@props._dragStart e,@
_dragEnd: (e) -> @props._dragEnd e,@
onDragEnd: (e) -> @props._dragEnd e,@
_keyDown: (e) ->
onKeyDown: (e) ->
@props._keyDown e,@
kc = e.keyCode
@ -35,7 +38,7 @@ module.exports = recl
if $el[0].tagName is 'TEXTAREA'
return $el.val()
else
if key is 'date-due'
if key is 'date_due'
d = $el.text().slice(1).replace(/\./g, "-")
return NaN if d.length < 8
return new Date(d).valueOf()
@ -50,13 +53,13 @@ module.exports = recl
compareVal: (l,n,key) ->
if key is 'tags' or key is 'audience'
return (_.xor(l,n).length > 0)
if key is 'date-due'
if key is 'date_due'
return l isnt new Date(n)
l isnt n
validateField: ($t,id,key,val) ->
validateField: ($t,key,val) ->
valid = 1
if key is 'date-due'
if key is 'date_due'
valid = 0 if isNaN(val)
if key is 'audience'
i = _.filter val,(a) ->
@ -71,39 +74,38 @@ module.exports = recl
valid = 0 if i.length isnt val.length
valid
_keyUp: (e) ->
onKeyUp: (e) ->
$t = $(e.target).closest '.field'
id = $t.closest('.item').attr 'data-id'
key = $t.attr 'data-key'
val = @getVal $t.find('.input'),key
if @compareVal @props.item[key],val,key
if not @validateField($t,id,key,val)
if not @validateField($t,key,val)
$t.addClass 'invalid'
return
$t.removeClass 'invalid'
if @to then clearTimeout @to
ver = @props.item.version
@to = setTimeout ->
WorkActions.setItem id,ver,key,val
@to = setTimeout =>
WorkActions.setItem @props.item,key,val
,1000
_focus: (e) -> @props._focus e,@
onFocus: (e) -> @props._focus e,@
_markDone: (e) ->
id = $(e.target).closest('.item').attr 'data-id'
WorkActions.setItem id,@props.item.version,'done',true
_markDone: (e) -> WorkActions.setItem @props.item,'done',true
_claim: (e) ->
_release: (e) ->
_changeStatus: (e) ->
return if @props.item.status is 'released'
if @props.item.status is 'accepted' and
@formatOwner(@props.item.owner) isnt window.urb.ship
return
own = "claim" if @props.item.status is "announced"
own = "announce" if @props.item.status is "accepted"
WorkActions.ownItem @props.item,own
_submitComment: (e) ->
$t = $(e.target).closest('.item')
id = $t.attr 'data-id'
val = $t.find('.comment .input').text()
WorkActions.addComment id,@props.item.version,val
WorkActions.addItem @props.item,val
formatDate: (d) ->
return "" if d is null
@ -118,93 +120,56 @@ module.exports = recl
getInitialState: -> {expand:false}
renderField: (key,props,format=_.identity)->
_props = _.extend {}, props, {contentEditable:true,className:'input ib'}
className = "#{props.className ? key} field ib"
(div {className,'data-key':key}, (div _props, format(@props.item[key])))
renderTopField: (key,props,format)->
_props = _.extend {className:"#{props.className ? key} top"}, props
@renderField key,_props,format
render: ->
itemClass = 'item'
if @state.expand then itemClass += ' expand'
owner = []
if @props.item.owner?.slice(1) is window.urb.ship
k = 'mine'
owner.push (div {className:'release a',onClick:@_release},'Release')
if @props.item.owner is null
k = 'open'
owner.push (div {className:'claim a',onClick:@_claim}, "Claim")
owner.unshift (div {className:k},@formatOwner(@props.item.owner))
action = ""
if @props.item.status is 'announced'
action = "claim"
if @props.item.status is 'accepted' and @formatOwner(@props.item.owner) is window.urb.ship
action = "release"
(div {
className:itemClass
draggable:true
'data-id':@props.item.id
'data-index':@props.index
onDragStart:@_dragStart
onDragEnd:@_dragEnd
className:itemClass
draggable:true
@onDragStart,@onDragEnd
}, [
(div {
className:'owner ib'
'data-key':'owner'
},owner)
(div {
className:'audience field ib'
'data-key':'audience'
className:'header'
},[
(div {
contentEditable:true
className:'input ib'
},@formatAudience(@props.item.audience))
(div {className:'owner ib'}, @formatOwner(@props.item.owner))
(div {
className:'status ib action-'+(action.length > 0)
'data-key':'status'
onClick:@_changeStatus
},[
(div {className:'label'}, @props.item.status)
(div {className:'action a'}, action)
])
(@renderField 'audience', {@onKeyUp}, @formatAudience) # no onKeyUp?
])
(div {className:'sort ib top'},@props.item.sort)
(div {
className:'done ib'
onClick:@_markDone
},'')
(div {
className:'title ib top field'
'data-key':'title'
},[
(div {
contentEditable:true
onFocus:@_focus
onKeyDown:@_keyDown
onKeyUp:@_keyUp
className:'input ib'
},@props.item.title)
])
(div {
className:'date ib top field'
'data-key':'date-due'
}, [
(div {
contentEditable:true
className:'input ib'
onKeyUp:@_keyUp
},@formatDate(@props.item['date-due']))
])
(div {
className:'tags ib top field'
'data-key':'tags'
},[
(div {
contentEditable:true
className:'input ib'
onKeyUp:@_keyUp
},@props.item.tags.join(" "))
])
(div {className:'sort ib top'}, @props.item.sort)
(div {className:'done ib', onClick:@_markDone}, '')
(@renderTopField 'title', {@onFocus,@onKeyDown,@onKeyUp})
(@renderTopField 'date_due', {@onKeyUp,className:'date'}, @formatDate)
(@renderTopField 'tags', {@onKeyUp}, (tags)-> tags.join(" "))
(div {
className:'expand ib',
onClick: (e) =>
@setState {expand:!@state.expand}
},[
(div {className:'caret left'},"")
])
(div {
className:'description field'
'data-key':'description'
},[
(textarea {
className:'input ib'
onKeyUp:@_keyUp
},@props.item.description)
])
onClick: (e) => @setState {expand:!@state.expand}
}, (div {className:'caret left'},"")
)
(@renderField 'description',{@onKeyUp})
(div {className:"hr"},"")
(div {className:"discussion"},[
(div {className:"comments"}, @props.item.discussion.map (slug) =>
@ -225,4 +190,4 @@ module.exports = recl
(div {className:'submit',onClick:@_submitComment},'Post')
])
])
])
])

View File

@ -1,6 +1,6 @@
recl = React.createClass
rece = React.createElement
[div,h1,input,textarea] = [React.DOM.div,React.DOM.h1,React.DOM.input,React.DOM.textarea]
{div,h1,input,textarea} = React.DOM
WorkStore = require '../stores/WorkStore.coffee'
WorkActions = require '../actions/WorkActions.coffee'
@ -11,8 +11,9 @@ FilterComponent = require './FilterComponent.coffee'
SortComponent = require './SortComponent.coffee'
module.exports = recl
stateFromStore: -> {
stateFromStore: -> window.canSort = WorkStore.canSort(); {
list:WorkStore.getList()
canSort:WorkStore.canSort()
listening:WorkStore.getListening()
sorts:WorkStore.getSorts()
filters:WorkStore.getFilters()
@ -31,11 +32,11 @@ module.exports = recl
_dragStart: (e,i) -> @dragged = i.dragged
_dragEnd: (e,i) ->
from = Number @dragged.attr('data-index')
to = Number @over.attr('data-index')
from = Number @dragged.closest('.item-wrap').attr('data-index')
to = Number @over.closest('.item-wrap').attr('data-index')
if from<to then to--
if @drop is 'after' then to++
WorkActions.swapItems to,from
WorkActions.moveItem (id for {id} in @state.list), to, from
@dragged.removeClass 'hidden'
@placeholder.remove()
@ -71,7 +72,7 @@ module.exports = recl
e.target.innerText.length is 0
if @state.selected isnt 0
@setState {selected:@state.selected-1,select:"end"}
WorkActions.removeItem @state.selected,@state.list[@state.selected].id
WorkActions.removeItem @state.list[@state.selected], @state.selected
e.preventDefault()
# up
when 38
@ -138,14 +139,15 @@ module.exports = recl
(div {
className:'items'
onDragOver:@_dragOver
}, [
_.map @state.list,(item,index) =>
rece(ItemComponent,{
item
index
@_focus
@_keyDown
@_dragStart
@_dragEnd})
])
}, _.map @state.list,(item,index) =>
(div {className:'item-wrap',key:item.id,'data-index':index},
rece(ItemComponent,{
item
@_focus
@_keyDown
draggable:@state.canSort
@_dragStart
@_dragEnd})
)
)
])

View File

@ -1,7 +1,7 @@
recl = React.createClass
rece = React.createElement
[div,h1,input,textarea] = [React.DOM.div,React.DOM.h1,React.DOM.input,React.DOM.textarea]
{div,h1,input,textarea} = React.DOM
module.exports = recl
render: ->
(div {className:'listening'}, "")
(div {className:'listening'}, "")

View File

@ -1,6 +1,6 @@
recl = React.createClass
rece = React.createElement
[div,h1,button,label] = [React.DOM.div,React.DOM.h1,React.DOM.button,React.DOM.label]
{div,h1,button,label} = React.DOM
module.exports = recl
_onClick: (e) ->
@ -22,4 +22,4 @@ module.exports = recl
(label {}, k)
(div {className:'caret ib'}, '')
])
)
)

View File

@ -1,6 +1,6 @@
recl = React.createClass
rece = React.createElement
[div,h1] = [React.DOM.div,React.DOM.h1]
{div,h1} = React.DOM
ListComponent = require './ListComponent.coffee'
@ -9,4 +9,4 @@ module.exports = recl
(div {}, [
(h1 {className:'leader'}, "Work")
(rece(ListComponent,{list:'upcoming'}))
])
])

View File

@ -6,22 +6,25 @@ Dispatcher = require('../dispatcher/Dispatcher.coffee');
Persistence = require('../persistence/Persistence.coffee');
module.exports = {
newItem: function(index, list) {
var item;
newItem: function(index, _item) {
var item, ref, ref1, ref2, ref3, ref4, ref5, ref6, ref7;
if (_item == null) {
_item = {};
}
item = {
id: window.util.uuid32(),
version: 0,
"date-created": Date.now(),
"date-modified": Date.now(),
"date-due": null,
done: null,
owner: window.urb.ship,
status: 'announced',
tags: [],
title: '',
description: '',
discussion: [],
audience: [window.util.talk.mainStationPath(window.urb.ship)]
date_created: Date.now(),
date_modified: Date.now(),
date_due: (ref = _item.date_due) != null ? ref : null,
done: (ref1 = _item.done) != null ? ref1 : null,
status: (ref2 = _item.status) != null ? ref2 : 'announced',
tags: (ref3 = _item.tags) != null ? ref3 : [],
title: (ref4 = _item.title) != null ? ref4 : '',
description: (ref5 = _item.description) != null ? ref5 : '',
discussion: (ref6 = _item.discussion) != null ? ref6 : [],
audience: (ref7 = _item.audience) != null ? ref7 : [window.util.talk.mainStationPath(window.urb.ship)]
};
Persistence.put({
"new": item
@ -32,10 +35,13 @@ module.exports = {
item: item
});
},
setItem: function(id, version, key, val) {
var set;
setItem: function(arg, key, val) {
var id, set, version;
id = arg.id, version = arg.version;
set = {};
key = key.split('_').join('-');
set[key] = val;
version += 1;
return Persistence.put({
old: {
id: id,
@ -46,7 +52,26 @@ module.exports = {
}
});
},
addComment: function(id, version, val) {
ownItem: function(arg, own) {
var id, o, version;
id = arg.id, version = arg.version;
o = {};
o[own] = null;
version += 1;
return Persistence.put({
old: {
id: id,
version: version,
dif: {
own: o
}
}
});
},
addItem: function(arg, val) {
var id, version;
id = arg.id, version = arg.version;
version += 1;
return Persistence.put({
old: {
id: id,
@ -73,17 +98,34 @@ module.exports = {
val: val
});
},
swapItems: function(to, from) {
moveItem: function(list, to, from) {
var sort;
sort = _.clone(list);
sort.splice(to, 0, sort.splice(from, 1)[0]);
Persistence.put({
sort: sort
});
return Dispatcher.handleViewAction({
type: 'swapItem',
list: sort,
to: to,
from: from,
to: to
type: 'moveItems'
});
},
removeItem: function(index, id) {
addItem: function(index, item) {
return Dispatcher.handleViewAction({
type: 'addItem',
index: index,
item: item
});
},
removeItem: function(arg, index) {
var id, version;
id = arg.id, version = arg.version;
Persistence.put({
old: {
id: id,
version: version,
dif: {
set: {
done: true
@ -96,13 +138,6 @@ module.exports = {
index: index
});
},
addItem: function(index, item) {
return Dispatcher.handleViewAction({
type: 'addItem',
index: index,
item: item
});
},
listenList: function(type) {
return Persistence.subscribe(type, function(err, d) {
var ref, sort, tasks;
@ -127,7 +162,7 @@ recl = React.createClass;
rece = React.createElement;
ref = [React.DOM.div, React.DOM.h1, React.DOM.label], div = ref[0], h1 = ref[1], label = ref[2];
ref = React.DOM, div = ref.div, h1 = ref.h1, label = ref.label;
module.exports = recl({
_onKeyDown: function(e) {
@ -211,23 +246,27 @@ var WorkActions, div, recl, ref, textarea;
recl = React.createClass;
ref = [React.DOM.div, React.DOM.textarea], div = ref[0], textarea = ref[1];
ref = React.DOM, div = ref.div, textarea = ref.textarea;
WorkActions = require('../actions/WorkActions.coffee');
module.exports = recl({
_dragStart: function(e) {
onDragStart: function(e) {
var $t;
if (!this.props.draggable) {
e.preventDefault();
return;
}
$t = $(e.target);
this.dragged = $t.closest('.item');
e.dataTransfer.effectAllowed = 'move';
e.dataTransfer.setData('text/html', e.currentTarget);
return this.props._dragStart(e, this);
},
_dragEnd: function(e) {
onDragEnd: function(e) {
return this.props._dragEnd(e, this);
},
_keyDown: function(e) {
onKeyDown: function(e) {
var kc;
this.props._keyDown(e, this);
kc = e.keyCode;
@ -253,7 +292,7 @@ module.exports = recl({
if ($el[0].tagName === 'TEXTAREA') {
return $el.val();
} else {
if (key === 'date-due') {
if (key === 'date_due') {
d = $el.text().slice(1).replace(/\./g, "-");
if (d.length < 8) {
return NaN;
@ -277,15 +316,15 @@ module.exports = recl({
if (key === 'tags' || key === 'audience') {
return _.xor(l, n).length > 0;
}
if (key === 'date-due') {
if (key === 'date_due') {
return l !== new Date(n);
}
return l !== n;
},
validateField: function($t, id, key, val) {
validateField: function($t, key, val) {
var i, valid;
valid = 1;
if (key === 'date-due') {
if (key === 'date_due') {
if (isNaN(val)) {
valid = 0;
}
@ -309,14 +348,13 @@ module.exports = recl({
}
return valid;
},
_keyUp: function(e) {
var $t, id, key, val, ver;
onKeyUp: function(e) {
var $t, key, val;
$t = $(e.target).closest('.field');
id = $t.closest('.item').attr('data-id');
key = $t.attr('data-key');
val = this.getVal($t.find('.input'), key);
if (this.compareVal(this.props.item[key], val, key)) {
if (!this.validateField($t, id, key, val)) {
if (!this.validateField($t, key, val)) {
$t.addClass('invalid');
return;
}
@ -324,28 +362,40 @@ module.exports = recl({
if (this.to) {
clearTimeout(this.to);
}
ver = this.props.item.version;
return this.to = setTimeout(function() {
return WorkActions.setItem(id, ver, key, val);
}, 1000);
return this.to = setTimeout((function(_this) {
return function() {
return WorkActions.setItem(_this.props.item, key, val);
};
})(this), 1000);
}
},
_focus: function(e) {
onFocus: function(e) {
return this.props._focus(e, this);
},
_markDone: function(e) {
var id;
id = $(e.target).closest('.item').attr('data-id');
return WorkActions.setItem(id, this.props.item.version, 'done', true);
return WorkActions.setItem(this.props.item, 'done', true);
},
_changeStatus: function(e) {
var own;
if (this.props.item.status === 'released') {
return;
}
if (this.props.item.status === 'accepted' && this.formatOwner(this.props.item.owner) !== window.urb.ship) {
return;
}
if (this.props.item.status === "announced") {
own = "claim";
}
if (this.props.item.status === "accepted") {
own = "announce";
}
return WorkActions.ownItem(this.props.item, own);
},
_claim: function(e) {},
_release: function(e) {},
_submitComment: function(e) {
var $t, id, val;
var $t, val;
$t = $(e.target).closest('.item');
id = $t.attr('data-id');
val = $t.find('.comment .input').text();
return WorkActions.addComment(id, this.props.item.version, val);
return WorkActions.addItem(this.props.item, val);
},
formatDate: function(d) {
if (d === null) {
@ -367,84 +417,82 @@ module.exports = recl({
expand: false
};
},
renderField: function(key, props, format) {
var _props, className, ref1;
if (format == null) {
format = _.identity;
}
_props = _.extend({}, props, {
contentEditable: true,
className: 'input ib'
});
className = ((ref1 = props.className) != null ? ref1 : key) + " field ib";
return div({
className: className,
'data-key': key
}, div(_props, format(this.props.item[key])));
},
renderTopField: function(key, props, format) {
var _props, ref1;
_props = _.extend({
className: ((ref1 = props.className) != null ? ref1 : key) + " top"
}, props);
return this.renderField(key, _props, format);
},
render: function() {
var itemClass, k, owner, ref1;
var action, itemClass;
itemClass = 'item';
if (this.state.expand) {
itemClass += ' expand';
}
owner = [];
if (((ref1 = this.props.item.owner) != null ? ref1.slice(1) : void 0) === window.urb.ship) {
k = 'mine';
owner.push(div({
className: 'release a',
onClick: this._release
}, 'Release'));
action = "";
if (this.props.item.status === 'announced') {
action = "claim";
}
if (this.props.item.owner === null) {
k = 'open';
owner.push(div({
className: 'claim a',
onClick: this._claim
}, "Claim"));
if (this.props.item.status === 'accepted' && this.formatOwner(this.props.item.owner) === window.urb.ship) {
action = "release";
}
owner.unshift(div({
className: k
}, this.formatOwner(this.props.item.owner)));
return div({
className: itemClass,
draggable: true,
'data-id': this.props.item.id,
'data-index': this.props.index,
onDragStart: this._dragStart,
onDragEnd: this._dragEnd
onDragStart: this.onDragStart,
onDragEnd: this.onDragEnd
}, [
div({
className: 'owner ib',
'data-key': 'owner'
}, owner), div({
className: 'audience field ib',
'data-key': 'audience'
className: 'header'
}, [
div({
contentEditable: true,
className: 'input ib'
}, this.formatAudience(this.props.item.audience))
className: 'owner ib'
}, this.formatOwner(this.props.item.owner)), div({
className: 'status ib action-' + (action.length > 0),
'data-key': 'status',
onClick: this._changeStatus
}, [
div({
className: 'label'
}, this.props.item.status), div({
className: 'action a'
}, action)
]), this.renderField('audience', {
onKeyUp: this.onKeyUp
}, this.formatAudience)
]), div({
className: 'sort ib top'
}, this.props.item.sort), div({
className: 'done ib',
onClick: this._markDone
}, ''), div({
className: 'title ib top field',
'data-key': 'title'
}, [
div({
contentEditable: true,
onFocus: this._focus,
onKeyDown: this._keyDown,
onKeyUp: this._keyUp,
className: 'input ib'
}, this.props.item.title)
]), div({
className: 'date ib top field',
'data-key': 'date-due'
}, [
div({
contentEditable: true,
className: 'input ib',
onKeyUp: this._keyUp
}, this.formatDate(this.props.item['date-due']))
]), div({
className: 'tags ib top field',
'data-key': 'tags'
}, [
div({
contentEditable: true,
className: 'input ib',
onKeyUp: this._keyUp
}, this.props.item.tags.join(" "))
]), div({
}, ''), this.renderTopField('title', {
onFocus: this.onFocus,
onKeyDown: this.onKeyDown,
onKeyUp: this.onKeyUp
}), this.renderTopField('date_due', {
onKeyUp: this.onKeyUp,
className: 'date'
}, this.formatDate), this.renderTopField('tags', {
onKeyUp: this.onKeyUp
}, function(tags) {
return tags.join(" ");
}), div({
className: 'expand ib',
onClick: (function(_this) {
return function(e) {
@ -453,19 +501,11 @@ module.exports = recl({
});
};
})(this)
}, [
div({
className: 'caret left'
}, "")
]), div({
className: 'description field',
'data-key': 'description'
}, [
textarea({
className: 'input ib',
onKeyUp: this._keyUp
}, this.props.item.description)
]), div({
}, div({
className: 'caret left'
}, "")), this.renderField('description', {
onKeyUp: this.onKeyUp
}), div({
className: "hr"
}, ""), div({
className: "discussion"
@ -519,7 +559,7 @@ recl = React.createClass;
rece = React.createElement;
ref = [React.DOM.div, React.DOM.h1, React.DOM.input, React.DOM.textarea], div = ref[0], h1 = ref[1], input = ref[2], textarea = ref[3];
ref = React.DOM, div = ref.div, h1 = ref.h1, input = ref.input, textarea = ref.textarea;
WorkStore = require('../stores/WorkStore.coffee');
@ -535,8 +575,10 @@ SortComponent = require('./SortComponent.coffee');
module.exports = recl({
stateFromStore: function() {
window.canSort = WorkStore.canSort();
return {
list: WorkStore.getList(),
canSort: WorkStore.canSort(),
listening: WorkStore.getListening(),
sorts: WorkStore.getSorts(),
filters: WorkStore.getFilters(),
@ -562,16 +604,25 @@ module.exports = recl({
return this.dragged = i.dragged;
},
_dragEnd: function(e, i) {
var from, to;
from = Number(this.dragged.attr('data-index'));
to = Number(this.over.attr('data-index'));
var from, id, to;
from = Number(this.dragged.closest('.item-wrap').attr('data-index'));
to = Number(this.over.closest('.item-wrap').attr('data-index'));
if (from < to) {
to--;
}
if (this.drop === 'after') {
to++;
}
WorkActions.swapItems(to, from);
WorkActions.moveItem((function() {
var j, len, ref1, results;
ref1 = this.state.list;
results = [];
for (j = 0, len = ref1.length; j < len; j++) {
id = ref1[j].id;
results.push(id);
}
return results;
}).call(this), to, from);
this.dragged.removeClass('hidden');
return this.placeholder.remove();
},
@ -621,7 +672,7 @@ module.exports = recl({
select: "end"
});
}
WorkActions.removeItem(this.state.selected, this.state.list[this.state.selected].id);
WorkActions.removeItem(this.state.list[this.state.selected], this.state.selected);
e.preventDefault();
}
break;
@ -707,20 +758,22 @@ module.exports = recl({
]), div({
className: 'items',
onDragOver: this._dragOver
}, [
_.map(this.state.list, (function(_this) {
return function(item, index) {
return rece(ItemComponent, {
item: item,
index: index,
_focus: _this._focus,
_keyDown: _this._keyDown,
_dragStart: _this._dragStart,
_dragEnd: _this._dragEnd
});
};
})(this))
])
}, _.map(this.state.list, (function(_this) {
return function(item, index) {
return div({
className: 'item-wrap',
key: item.id,
'data-index': index
}, rece(ItemComponent, {
item: item,
_focus: _this._focus,
_keyDown: _this._keyDown,
draggable: _this.state.canSort,
_dragStart: _this._dragStart,
_dragEnd: _this._dragEnd
}));
};
})(this)))
]);
}
});
@ -734,7 +787,7 @@ recl = React.createClass;
rece = React.createElement;
ref = [React.DOM.div, React.DOM.h1, React.DOM.input, React.DOM.textarea], div = ref[0], h1 = ref[1], input = ref[2], textarea = ref[3];
ref = React.DOM, div = ref.div, h1 = ref.h1, input = ref.input, textarea = ref.textarea;
module.exports = recl({
render: function() {
@ -753,7 +806,7 @@ recl = React.createClass;
rece = React.createElement;
ref = [React.DOM.div, React.DOM.h1, React.DOM.button, React.DOM.label], div = ref[0], h1 = ref[1], button = ref[2], label = ref[3];
ref = React.DOM, div = ref.div, h1 = ref.h1, button = ref.button, label = ref.label;
module.exports = recl({
_onClick: function(e) {
@ -799,7 +852,7 @@ recl = React.createClass;
rece = React.createElement;
ref = [React.DOM.div, React.DOM.h1], div = ref[0], h1 = ref[1];
ref = React.DOM, div = ref.div, h1 = ref.h1;
ListComponent = require('./ListComponent.coffee');
@ -1251,7 +1304,7 @@ module.exports = {
},{}],15:[function(require,module,exports){
var Dispatcher, EventEmitter, WorkStore, _filters, _list, _listening, _sorts, assign;
var Dispatcher, EventEmitter, WorkStore, _filters, _list, _listening, _sorts, _tasks, assign;
EventEmitter = require('events').EventEmitter;
@ -1259,17 +1312,17 @@ assign = require('object-assign');
Dispatcher = require('../dispatcher/Dispatcher.coffee');
_list = [
{
_tasks = {
"0v0": {
id: "0v0",
version: 0,
sort: 0,
"date-created": new Date('2015-8-18'),
"date-modified": new Date('2015-8-18'),
"date-due": new Date('2015-8-18'),
owner: "~zod",
date_created: new Date('2015-8-18'),
date_modified: new Date('2015-8-18'),
date_due: new Date('2015-8-18'),
owner: "zod",
audience: ["~doznec/urbit-meta", "~doznec/tlon"],
status: "working",
status: "announced",
tags: ['food', 'office'],
title: 'get groceries',
description: 'first go out the door, \n then walk down the block.',
@ -1280,36 +1333,40 @@ _list = [
body: "Seems like a great idea."
}
]
}, {
},
"0v1": {
id: "0v1",
version: 0,
sort: 1,
"date-created": new Date('2015-8-18'),
"date-modified": new Date('2015-8-18'),
"date-due": null,
owner: null,
date_created: new Date('2015-8-18'),
date_modified: new Date('2015-8-18'),
date_due: null,
owner: "~zod",
audience: ["~doznec/tlon"],
status: "working",
status: "accepted",
tags: ['home', 'office'],
title: 'eat',
description: 'dont forget about lunch.',
discussion: []
}, {
},
"0v2": {
id: "0v2",
version: 0,
sort: 2,
"date-created": new Date('2015-8-18'),
"date-modified": new Date('2015-8-18'),
"date-due": null,
owner: "~talsur-todres",
date_created: new Date('2015-8-18'),
date_modified: new Date('2015-8-18'),
date_due: null,
owner: "talsur-todres",
audience: ["~doznec/tlon"],
status: "working",
status: "accepted",
tags: ['home'],
title: 'sleep',
description: 'go get some sleep.',
discussion: []
}
];
};
_list = ["0v0", "0v1", "0v2"];
_listening = [];
@ -1323,7 +1380,7 @@ _filters = {
_sorts = {
title: 0,
owner: 0,
"date-due": 0,
date_due: 0,
sort: 0
};
@ -1338,52 +1395,44 @@ WorkStore = assign({}, EventEmitter.prototype, {
return this.removeListener("change", cb);
},
getData: function(arg) {
var _tasks, got, i, id, j, len, sort, tasks;
var sort, tasks;
sort = arg.sort, tasks = arg.tasks;
_tasks = _.clone(tasks);
for (i = j = 0, len = _list.length; j < len; i = ++j) {
id = _list[i].id;
if (!(got = _tasks[id])) {
continue;
}
delete _tasks[id];
_list[i] = this.itemFromData(got, i);
}
return sort.map((function(_this) {
return function(k, index) {
if (_tasks[k]) {
return _this.newItem({
item: _tasks[k],
index: index
});
return function(id, index) {
if (!_tasks[id]) {
_list.splice(index, 0, id);
}
if (tasks[id]) {
return _tasks[id] = _this.itemFromData(tasks[id], index);
}
};
})(this));
},
getList: function(key) {
var _k, _v, add, c, k, list, v;
var add, atr, c, i, id, k, len, list, task, v;
list = [];
for (k in _list) {
v = _list[k];
for (i = 0, len = _list.length; i < len; i++) {
id = _list[i];
task = _tasks[id];
add = true;
for (_k in _filters) {
_v = _filters[_k];
if (_v === null) {
for (atr in _filters) {
v = _filters[atr];
if (v === null) {
continue;
}
c = v[_k];
c = task[atr];
if (typeof c === 'object') {
if (_.intersection(c, _v).length === 0) {
if (_.intersection(c, v).length === 0) {
add = false;
}
} else {
if (c !== _v) {
if (c !== v) {
add = false;
}
}
}
if (add === true) {
list.push(v);
list.push(task);
}
}
if (_.uniq(_.values(_sorts)).length > 0) {
@ -1423,6 +1472,18 @@ WorkStore = assign({}, EventEmitter.prototype, {
}
return _sorts[key] = val;
},
canSort: function() {
var k, v;
for (k in _sorts) {
v = _sorts[k];
if (k === "sort" && v === 1) {
return true;
} else if (v !== 0) {
return false;
}
}
return true;
},
itemFromData: function(item, index) {
var _item;
if (index == null) {
@ -1431,25 +1492,30 @@ WorkStore = assign({}, EventEmitter.prototype, {
_item = _.extend({
sort: index
}, item);
_item["date-modified"] = new Date(item["date-modified"]);
_item["date-created"] = new Date(item["date-created"]);
if (item["date-due"] != null) {
_item["date-due"] = new Date(item["date-due"]);
_item.date_modified = new Date(item.date_modified);
_item.date_created = new Date(item.date_created);
if (item.date_due != null) {
_item.date_due = new Date(item.date_due);
}
if (item.done != null) {
_item.done = new Date(item.done);
}
_item.discussion = item.discussion.map(function(arg) {
var body, date, ship;
ship = arg.ship, body = arg.body, date = arg.date;
return {
ship: ship,
body: body,
date: new Date(date)
};
});
return _item;
},
newItem: function(arg) {
var index, item;
item = arg.item, index = arg.index;
return _list.splice(index, 0, this.itemFromData(item, index));
},
swapItem: function(arg) {
var from, to;
to = arg.to, from = arg.from;
return _list.splice(to, 0, _list.splice(from, 1)[0]);
moveItems: function(arg) {
var from, list, to;
list = arg.list, to = arg.to, from = arg.from;
_tasks[_list[from]].sort = _tasks[_list[to]].sort;
return _list = list;
},
removeItem: function(arg) {
var index;

View File

@ -2,16 +2,17 @@ EventEmitter = require('events').EventEmitter
assign = require 'object-assign'
Dispatcher = require '../dispatcher/Dispatcher.coffee'
_list = [
_tasks =
"0v0":
id:"0v0"
version:0
sort:0
"date-created":new Date('2015-8-18')
"date-modified":new Date('2015-8-18')
"date-due":new Date('2015-8-18')
owner:"~zod"
date_created:new Date('2015-8-18')
date_modified:new Date('2015-8-18')
date_due:new Date('2015-8-18')
owner:"zod"
audience:["~doznec/urbit-meta","~doznec/tlon"]
status:"working"
status:"announced"
tags:['food','office']
title:'get groceries'
description:'first go out the door, \n then walk down the block.'
@ -22,35 +23,38 @@ _list = [
body:"Seems like a great idea."
}
]
,
"0v1":
id:"0v1"
version:0
sort:1
"date-created":new Date('2015-8-18')
"date-modified":new Date('2015-8-18')
"date-due":null
owner:null
date_created:new Date('2015-8-18')
date_modified:new Date('2015-8-18')
date_due:null
owner:"~zod"
audience:["~doznec/tlon"]
status:"working"
status:"accepted"
tags:['home','office']
title:'eat'
description:'dont forget about lunch.'
discussion:[]
,
"0v2":
id:"0v2"
version:0
sort:2
"date-created":new Date('2015-8-18')
"date-modified":new Date('2015-8-18')
"date-due":null
owner:"~talsur-todres"
date_created:new Date('2015-8-18')
date_modified:new Date('2015-8-18')
date_due:null
owner:"talsur-todres"
audience:["~doznec/tlon"]
status:"working"
status:"accepted"
tags:['home']
title:'sleep'
description:'go get some sleep.'
discussion:[]
]
_list = ["0v0","0v1","0v2"]
_listening = []
_filters =
owner:null
@ -60,7 +64,7 @@ _filters =
_sorts =
title:0
owner:0
"date-due":0
date_due:0
sort:0
WorkStore = assign {},EventEmitter.prototype,{
@ -69,27 +73,26 @@ WorkStore = assign {},EventEmitter.prototype,{
removeChangeListener: (cb) -> @removeListener "change", cb
getData: ({sort,tasks})->
_tasks = _.clone tasks
for {id},i in _list when got = _tasks[id]
delete _tasks[id]
_list[i] = @itemFromData got,i
sort.map (k,index)=>
if _tasks[k]
@newItem {item:_tasks[k],index}
sort.map (id,index)=>
unless _tasks[id]
_list.splice index, 0, id
if tasks[id] # XX client-side defaults
_tasks[id] = @itemFromData tasks[id], index
getList: (key) ->
list = []
for k,v of _list
for id in _list
task = _tasks[id]
add = true
for _k,_v of _filters
if _v is null then continue
c = v[_k]
for atr,v of _filters
if v is null then continue
c = task[atr]
if typeof(c) is 'object'
if _.intersection(c,_v).length is 0 then add = false
if _.intersection(c,v).length is 0 then add = false
else
if c isnt _v then add = false
if c isnt v then add = false
if add is true
list.push v
list.push task
if _.uniq(_.values(_sorts)).length > 0
for k,v of _sorts
if v isnt 0
@ -99,30 +102,36 @@ WorkStore = assign {},EventEmitter.prototype,{
list
getListening: -> _listening
getFilters: -> _filters
setFilter: ({key,val}) -> _filters[key] = val
getSorts: -> _sorts
setSort: ({key,val}) ->
for k,v of _sorts
_sorts[k] = 0
_sorts[key] = val
canSort: ->
for k,v of _sorts
if k is "sort" and v is 1
return true
else if v isnt 0
return false
true
itemFromData: (item,index=0)->
_item = _.extend {sort:index}, item
_item["date-modified"] = new Date item["date-modified"]
_item["date-created"] = new Date item["date-created"]
_item["date-due"] = new Date item["date-due"] if item["date-due"]?
_item.done = new Date item.done if item.done?
_item.date_modified = new Date item.date_modified
_item.date_created = new Date item.date_created
_item.date_due = new Date item.date_due if item.date_due?
_item.done = new Date item.done if item.done?
_item.discussion = item.discussion.map ({ship,body,date}) ->
{ship,body,date: new Date date}
_item
newItem: ({item,index}) -> _list.splice index,0,@itemFromData item,index
swapItem: ({to,from}) -> _list.splice to,0,_list.splice(from,1)[0]
removeItem: ({index}) -> _list.splice index,1
moveItems: ({list,to,from}) ->
_tasks[_list[from]].sort = _tasks[_list[to]].sort
_list = list
removeItem: ({index}) -> _list.splice index,1
}