here comes talk

This commit is contained in:
Galen Wolfe-Pauly 2015-03-26 14:00:45 -07:00
commit 70e73f4f03
21 changed files with 1234 additions and 886 deletions

View File

@ -1989,7 +1989,7 @@ try to get the aeon referred to by the starting case. If it doesn't
exist yet, then we can't do anything interesting with this subscription,
so we move on to the next one.
Otherwise, we try to get the aeon referrred to by the ending case. If it
Otherwise, we try to get the aeon referred to by the ending case. If it
doesn't exist yet, then we produce all the information we can. We call
`++lobes-at-path` at the given aeon and path to see if the requested
path has actually changed. If it hasn't, then we don't produce anything;

View File

@ -0,0 +1,12 @@
# installing
`npm install`
# building
in `src/js/`:
`watchify -v -t coffeeify -o main.js main.coffee`
in `src/css/`:
`stylus -w main.styl`

View File

@ -9,8 +9,7 @@
;script(type "text/javascript", src "//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.1/jquery.js");
;script(type "text/javascript", src "//cdnjs.cloudflare.com/ajax/libs/lodash.js/2.4.1/lodash.min.js");
;script(type "text/javascript", src "//cdnjs.cloudflare.com/ajax/libs/react/0.12.1/react.js");
;script(type "text/javascript", src "/main/pub/talk/src/js/dep/director.js");
;meta(name "viewport", content "width=432, initial-scale=1");
;meta(name "viewport", content "width=device-width, height=device-height, initial-scale=1.0, user-scalable=0, minimum-scale=1.0, maximum-scale=1.0");
;link(type "text/css", rel "stylesheet", href "/main/pub/talk/src/css/main.css");
;script(type "text/javascript", src "/gop/hart.js");
;title: Radio

View File

@ -70,3 +70,15 @@
font-weight: 500;
font-style: normal;
}
@font-face {
font-family: "scp";
src: url("http://storage.googleapis.com/urbit-extra/scp-bold.woff");
font-weight: 600;
font-style: normal;
}
@font-face {
font-family: "scp";
src: url("http://storage.googleapis.com/urbit-extra/scp-black.woff");
font-weight: 700;
font-style: normal;
}

View File

@ -70,10 +70,25 @@
font-weight: 500;
font-style: normal;
}
@font-face {
font-family: "scp";
src: url("http://storage.googleapis.com/urbit-extra/scp-bold.woff");
font-weight: 600;
font-style: normal;
}
@font-face {
font-family: "scp";
src: url("http://storage.googleapis.com/urbit-extra/scp-black.woff");
font-weight: 700;
font-style: normal;
}
.iden,
.audi,
.time,
#length {
#length,
#where,
#who,
.station {
font-family: "scp";
}
.join-ctrl input {
@ -100,6 +115,9 @@ input.join,
#station {
font-size: 0.7rem;
}
.iden {
font-weight: 600;
}
html,
body {
font-size: 18px;
@ -113,124 +131,152 @@ body {
display: none;
}
#c {
top: 0;
background-color: #fff;
}
#stations-container {
position: absolute;
top: 1rem;
top: 0rem;
left: 50%;
width: 24rem;
margin-left: -12rem;
font-size: 4rem;
width: 58rem;
margin-left: -29rem;
margin-bottom: 12rem;
}
#station-container {
position: fixed;
top: 0;
left: 50%;
width: 28rem;
max-height: 2.6rem;
width: 58rem;
max-height: 2.7rem;
background-color: #fff;
border-bottom: 0.3rem solid #000;
padding-bottom: 1rem;
overflow: hidden;
margin-left: -14rem;
padding-top: 1rem;
background-color: #f5f5f5;
border-bottom: 3px solid #ededed;
transition: max-height 0.15s ease-out;
z-index: 10;
-webkit-transition: max-height 0.2s;
}
#station-container:hover {
max-height: 12rem;
transition: max-height 0.25s ease-in;
max-height: 30rem;
height: auto;
-webkit-transition: max-height 0.2s;
}
#stations-container,
#messages-container {
vertical-align: top;
#station #station-container {
padding-top: 1rem;
min-width: 30rem;
text-align: right;
}
#messaging-container {
position: absolute;
top: 4rem;
left: 50%;
width: 24rem;
margin-left: -12rem;
margin-bottom: 4rem;
}
#station > div {
display: inline-block;
}
#station-meta {
margin-right: 1rem;
#writing-container {
bottom: 4rem;
margin-bottom: 1rem;
}
#sources-container {
width: 6rem;
float: right;
margin: 1rem -6rem 0 0;
#messages-container {
vertical-align: top;
margin-top: 4rem;
}
#members {
margin-left: 2rem;
}
#station .iden {
display: block;
}
.station {
.caret,
.circle {
display: inline-block;
width: 9rem;
margin-bottom: 0.3rem;
cursor: pointer;
font-weight: 200;
}
#stations .station {
display: block;
width: 24rem;
}
.station .name {
padding: 0.3rem;
.caret {
width: 0;
height: 0;
border-top: 6px solid transparent;
border-right: 6px solid transparent;
border-bottom: 6px solid transparent;
border-left: 6px solid #000;
}
.station .name:hover {
background-color: #f5f5f5;
border-bottom: 6px solid #ededed;
.circle {
width: 6px;
height: 6px;
border: 3px solid #000;
border-radius: 6px;
}
.station > div {
#station-container #where .caret {
-webkit-transform: rotate(0deg);
-webkit-transform-origin: 0 50%;
-webkit-transition: -webkit-transform 0.2s;
}
#station-container:hover #where .caret {
transform: rotate(90deg);
transform-origin: 0 50%;
-webkit-transform: rotate(90deg);
-webkit-transition: -webkit-transform 0.2s;
}
#head {
width: 100%;
height: 4rem;
}
#station-container h1 {
font-weight: 500;
font-size: 0.6rem;
letter-spacing: 0.06rem;
display: inline-block;
line-height: 1.2rem;
}
#audience,
#who {
width: 34.6rem;
}
#stations,
#where {
width: 17.3rem;
}
#stations,
#audience,
#head > div {
display: inline-block;
text-align: right;
vertical-align: top;
}
#head > div {
height: 2rem;
margin-top: 1.5rem;
}
#where .caret {
border-left-color: #f00;
margin-left: 1rem;
}
#who > span {
margin-left: 1rem;
}
#members > div {
display: block;
}
#members > div div {
display: inline-block;
}
.toggle {
width: 0.4rem;
height: 0.4rem;
border: 2px solid #000;
margin-right: 0.6rem;
#members .audi {
margin-right: 0.3rem;
}
.toggle.active {
background-color: #000;
#members .iden {
min-width: 6rem;
}
.station div {
display: inline-block;
}
.station .remove {
display: none;
float: right;
margin-left: 1rem;
opacity: 0;
cursor: pointer;
width: 0.6rem;
margin-right: 0.6rem;
font-weight: 600;
color: #f00;
}
.station:hover .remove {
display: inline;
opacity: 1;
}
#sources-container .station {
font-family: "scp";
font-weight: 500;
text-transform: lowercase;
}
.sour-ctrl {
margin-bottom: 0.16rem;
}
.join-ctrl input,
.sour-ctrl input {
font-family: "scp";
border: none;
font-weight: 400;
text-align: right;
line-height: 1rem;
outline: none;
cursor: pointer;
}
.sour-ctrl input::-webkit-input-placeholder {
font-family: "bau";
font-family: "scp";
font-size: 1rem;
font-weight: 200;
font-weight: 600;
margin-left: 0.6rem;
color: #0003ff;
color: #000;
}
.sour-ctrl input:focus::-webkit-input-placeholder {
color: #fff;
}
.message {
padding-top: 0.3rem;
@ -245,8 +291,9 @@ body {
#messages .message:hover .time {
opacity: 1;
}
.time {
margin-right: 0.6rem;
#messages .message .ship,
#messages .message .audi > div {
cursor: pointer;
}
.member {
width: 12rem;
@ -255,55 +302,82 @@ body {
#messages {
height: auto;
}
.message.pending {
color: #ccc;
}
.message.say .mess {
font-style: italic;
}
.mess,
.iden,
.attr > div,
#station .member div,
#writing {
display: inline-block;
}
.iden > div {
display: inline;
}
.mess,
#writing,
#length {
vertical-align: top;
}
.attr > div {
max-width: 16rem;
}
.attr {
color: #d7d7d7;
text-align: right;
display: inline-block;
margin-right: 0.3rem;
min-width: 16rem;
}
.attr .iden {
color: #000;
margin-left: 0.3rem;
}
.attr > div {
.audi {
white-space: nowrap;
}
.audi > div {
margin-right: 0.3rem;
max-width: 8rem;
}
.iden > div {
max-width: 16rem;
}
.iden > div,
.audi > div {
display: inline-block;
background-color: #fff;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
-webkit-transition: max-height 0.2s;
}
.audi > div:last-child {
margin-right: 0;
}
.iden > div:hover,
.audi > div:hover {
position: relative;
max-width: 30rem;
-webkit-transition: max-height 0.2s;
}
.mess {
font-size: 0.9rem;
line-height: 1.6rem;
letter-spacing: 0.03rem;
word-wrap: break-word;
max-width: 31rem;
max-width: 30rem;
margin-left: 0.3rem;
}
.ship {
font-weight: 600;
#writing {
font-size: 0.9rem;
min-height: 1.6rem;
min-width: 1.3rem;
outline: none;
background-color: #eee;
padding: 0.3rem 0.1rem;
margin-left: 0.2rem;
margin-top: -0.3rem;
}
.ship.talk:before {
content: "...";
margin-left: -1.3rem;
margin-right: 0.3rem;
width: 1rem;
margin-top: -0.4rem;
vertical-align: middle;
display: inline-block;
line-height: 0.2rem;
letter-spacing: -0.1rem;
}
#writing-container {
bottom: 4rem;
margin-bottom: 1rem;
float: left;
#writing:focus {
background-color: #fff;
}
.writing {
padding-top: 0.3rem;
@ -313,60 +387,25 @@ body {
margin-left: 1rem;
margin-top: 1.2rem;
}
#writing {
font-size: 0.9rem;
min-height: 1.6rem;
line-height: 1.6rem;
min-width: 1.3rem;
padding: 0;
outline: none;
.writing .iden {
display: block;
text-align: right;
width: 100%;
}
.writing .iden .ship {
margin-right: 0.3rem;
}
#audi {
display: inline-block;
background-color: #eee;
}
#writing:focus {
background-color: #fff;
}
#station h1 {
display: inline-block;
margin: 0;
font-weight: 200;
font-size: 2rem;
text-transform: lowercase;
}
input.join {
font-size: 4rem;
background-color: #fff;
padding: 0.3rem 0.1rem;
margin-top: -0.3rem;
margin-right: -0.2rem;
outline: none;
width: 24rem;
overflow: hidden;
}
#station input.join {
font-family: "scp";
font-size: 0.7rem;
line-height: 1rem;
width: 12rem;
}
input.join::-webkit-input-placeholder {
color: #0003ff;
}
input.join:focus::-webkit-input-placeholder {
color: #fff;
}
.pending {
color: #ccc;
}
a.up {
height: 2rem;
margin-top: 0.6rem;
vertical-align: middle;
display: inline-block;
}
.arow-up {
display: inline-block;
margin: 0 0.5rem 0 0.5rem;
width: 0;
height: 0;
border-left: 9px solid transparent;
border-right: 9px solid transparent;
border-bottom: 9px solid #000;
#audi.valid-false {
color: #ff2f2f;
}
#scrolling {
display: none;
@ -383,67 +422,45 @@ a.up {
font-size: 0.8rem;
text-transform: uppercase;
}
@media (max-width: 40rem) {
#c {
@media only screen and (max-width: 1170px) {
#c,
#station-container {
width: 96%;
left: 0;
margin-left: 0;
width: 24rem;
margin-left: 2%;
margin-right: 2%;
}
#messages-container,
#writing-container {
margin-left: 1rem;
.mess,
#writing {
max-width: 40%;
line-height: 1.2rem;
}
#stations-container,
#station-container {
position: relative;
float: left;
.attr,
#stations,
#where {
width: 20%;
min-width: 20%;
}
#stations-container {
width: 8rem;
}
#station-container {
left: auto;
}
.station {
width: 5rem;
}
.attr {
display: block;
text-align: left;
width: 2rem;
margin-right: 1rem;
}
.message {
height: 1.6rem;
}
.stations,
.iden,
#station {
font-size: 0.5rem;
}
.station .remove {
display: inline;
font-size: 0.6rem;
line-height: 0.6rem;
}
.ship.talk:before {
margin-left: -0.3rem;
margin-right: 0;
}
.attr {
width: 4rem;
}
.iden > div {
display: block;
}
.attr > .time {
display: none;
}
.mess {
max-width: 12rem;
margin-bottom: 1rem;
#audience,
#who {
width: 60%;
}
#writing {
max-width: 12rem;
padding: 0.1rem;
}
.m-down,
.m-up {
position: absolute;
}
.m-down.m-fixed {
position: fixed;
top: 0;
}
}
@media only screen and (min-device-width: 320px) and (max-device-width: 480px) {
.mess,
#writing {
max-width: 70%;
line-height: 1.2rem;
}
}

View File

@ -1,9 +1,16 @@
//
// fonts first
//
@import 'fonts'
.iden
.audi
.time
#length
#where
#who
.station
font-family "scp"
.join-ctrl input
@ -30,10 +37,17 @@ input.join
#station
font-size .7rem
.iden
font-weight 600
html
body
font-size 18px
//
// containers
//
body
background-color #fefefe
padding 0
@ -43,125 +57,165 @@ body
display none
#c
top 0
background-color #fff
#stations-container
position absolute
top 1rem
top 0rem
left 50%
width 24rem
margin-left -12rem
font-size 4rem
width 58rem
margin-left -29rem
margin-bottom 12rem
#station-container
position fixed
top 0
left 50%
width 28rem
max-height 2.6rem
width 58rem
max-height 2.7rem
background-color white
border-bottom .3rem solid black
padding-bottom 1rem
overflow hidden
margin-left -14rem
padding-top 1rem
background-color #f5f5f5
border-bottom 3px solid #ededed
transition max-height 0.15s ease-out
z-index 10
-webkit-transition max-height .2s
#station-container:hover
max-height 12rem
transition max-height 0.25s ease-in
max-height 30rem
height auto
-webkit-transition max-height .2s
#stations-container
#messages-container
vertical-align top
#station #station-container
padding-top 1rem
min-width 30rem
text-align right
#messaging-container
position absolute
top 4rem
left 50%
width 24rem
margin-left -12rem
margin-bottom 4rem
#station > div
display inline-block
#station-meta
margin-right 1rem
#writing-container
bottom 4rem
margin-bottom 1rem
#sources-container
width 6rem
float right
margin 1rem -6rem 0 0
#messages-container
vertical-align top
margin-top 4rem
#members
margin-left 2rem
//
// components
//
#station .iden
display block
.station
.caret
.circle
display inline-block
width 9rem
margin-bottom .3rem
cursor pointer
font-weight 200
#stations .station
display block
width 24rem
.station .name
padding .3rem
.caret
width 0
height 0
border-top 6px solid transparent
border-right 6px solid transparent
border-bottom 6px solid transparent
border-left 6px solid #000
.station .name:hover
background-color #f5f5f5
border-bottom 6px solid #ededed
.circle
width 6px
height 6px
border 3px solid black
border-radius 6px
.station > div
//
// station
//
#station-container #where .caret
-webkit-transform rotate(0deg)
-webkit-transform-origin 0 50%
-webkit-transition -webkit-transform .2s
#station-container:hover #where .caret
transform rotate(90deg)
transform-origin 0 50%
-webkit-transform rotate(90deg)
-webkit-transition -webkit-transform .2s
#head
width 100%
height 4rem
#station-container h1
font-weight 500
font-size .6rem
letter-spacing .06rem
display inline-block
line-height 1.2rem
#audience
#who
width 34.6rem
#stations
#where
width 17.3rem
#stations
#audience
#head > div
display inline-block
text-align right
vertical-align top
#head > div
height 2rem
margin-top 1.5rem
#where .caret
border-left-color red
margin-left 1rem
#who > span
margin-left 1rem
#members > div
display block
#members > div div
display inline-block
.toggle
width .4rem
height .4rem
border 2px solid #000
margin-right .6rem
#members .audi
margin-right .3rem
.toggle.active
background-color #000
#members .iden
min-width 6rem
.station div
display inline-block
.station .remove
display none
float right
margin-left 1rem
opacity 0
cursor pointer
width .6rem
margin-right .6rem
font-weight 600
color #ff0000
.station:hover .remove
display inline
opacity 1
#sources-container .station
font-family "scp"
font-weight 500
text-transform lowercase
// hate this.
.sour-ctrl
margin-bottom .16rem
.join-ctrl input
// add source
.sour-ctrl input
font-family "scp"
border none
font-weight 400
text-align right
line-height 1rem
outline none
cursor pointer
.sour-ctrl input::-webkit-input-placeholder
font-family "bau"
font-family "scp"
font-size 1rem
font-weight 200
font-weight 600
margin-left .6rem
color #0003FF
color #000
.sour-ctrl input:focus::-webkit-input-placeholder
color white
//
// messages
//
.message
padding-top .3rem
@ -176,8 +230,9 @@ body
#messages .message:hover .time
opacity 1
.time
margin-right .6rem
#messages .message .ship
#messages .message .audi > div
cursor pointer
.member
width 12rem
@ -186,55 +241,86 @@ body
#messages
height auto
.message.pending
color #ccc
.message.say .mess
font-style italic
.mess
.iden
.attr > div
#station .member div
#writing
display inline-block
.iden > div
display inline
.mess
#writing
#length
vertical-align top
.attr > div
max-width 16rem
.attr
color #D7D7D7
text-align right
display inline-block
margin-right .3rem
min-width 16rem
.attr .iden
color #000
margin-left .3rem
.attr > div
.audi
white-space nowrap
.audi > div
margin-right .3rem
max-width 8rem
.iden > div
max-width 16rem
.iden > div
.audi > div
display inline-block
background-color white
overflow hidden
white-space nowrap
text-overflow ellipsis
-webkit-transition max-height .2s
.audi > div:last-child
margin-right 0
.iden > div:hover
.audi > div:hover
position relative
max-width 30rem
-webkit-transition max-height .2s
.mess
font-size .9rem
line-height 1.6rem
letter-spacing .03rem
word-wrap break-word
max-width 31rem
max-width 30rem
margin-left .3rem
.ship
font-weight 600
//
// writing
//
.ship.talk:before
content "..."
margin-left -1.3rem
margin-right .3rem
width 1rem
margin-top -.4rem
vertical-align middle
display inline-block
line-height .2rem
letter-spacing -.1rem
#writing
font-size .9rem
min-height 1.6rem
min-width 1.3rem
outline none
background-color #eee
padding .3rem .1rem
margin-left .2rem
margin-top -.3rem
#writing-container
bottom 4rem
margin-bottom 1rem
float left
#writing:focus
background-color #fff
.writing
padding-top .3rem
@ -244,60 +330,29 @@ body
margin-left 1rem
margin-top 1.2rem
#writing
font-size .9rem
min-height 1.6rem
line-height 1.6rem
min-width 1.3rem
padding 0
outline none
.writing .iden
display block
text-align right
width 100%
.writing .iden .ship
margin-right .3rem
#audi
display inline-block
background-color #eee
#writing:focus
background-color #fff
#station h1
display inline-block
margin 0
font-weight 200
font-size 2rem
text-transform lowercase
input.join
font-size 4rem
background-color #fff
padding .3rem .1rem
margin-top -.3rem
margin-right -.2rem
outline none
width 24rem
overflow hidden
#station input.join
font-family "scp"
font-size .7rem
line-height 1rem
width 12rem
#audi.valid-false
color #ff2f2f
input.join::-webkit-input-placeholder
color #0003FF
input.join:focus::-webkit-input-placeholder
color #fff
.pending
color #ccc
a.up
height 2rem
margin-top .6rem
vertical-align middle
display inline-block
.arow-up
display inline-block
margin 0 .5rem 0 .5rem
width 0
height 0
border-left 9px solid transparent
border-right 9px solid transparent
border-bottom 9px solid #000
//
// scrolling
//
#scrolling
display none
@ -314,66 +369,4 @@ a.up
font-size .8rem
text-transform uppercase
@media (max-width: 40rem)
#c
left 0
margin-left 0
width 24rem
#messages-container
#writing-container
margin-left 1rem
#stations-container
#station-container
position relative
float left
#stations-container
width 8rem
#station-container
left auto
.station
width 5rem
.attr
display block
text-align left
width 2rem
margin-right 1rem
.message
height 1.6rem
.stations
.iden
#station
font-size .5rem
.station .remove
display inline
font-size .6rem
line-height .6rem
.ship.talk:before
margin-left -.3rem
margin-right 0
.attr
width 4rem
.iden > div
display block
.attr > .time
display none
.mess
max-width 12rem
margin-bottom 1rem
#writing
max-width 12rem
@import 'mobile'

View File

@ -0,0 +1,44 @@
/* laptops / small screens ----------- */
@media only screen and (max-width: 1170px)
#c
#station-container
width 96%
left 0
margin-left 2%
margin-right 2%
.mess
#writing
max-width 40%
line-height 1.2rem
.attr
#stations
#where
width 20%
min-width 20%
#audience
#who
width 60%
#writing
padding .1rem
.m-down
.m-up
position absolute
.m-down.m-fixed
position fixed
top 0
/* tablets + phones ----------- */
// @media only screen and (min-width: 320px) and (max-width: 1024px)
/* phones portrait and landscape ----------- */
@media only screen and (min-device-width: 320px) and (max-device-width: 480px)
.mess
#writing
max-width 70%
line-height 1.2rem

View File

@ -1,7 +1,5 @@
MessageDispatcher = require '../dispatcher/Dispatcher.coffee'
# hm
module.exports =
loadMessages: (grams,get) ->
MessageDispatcher.handleServerAction
@ -29,12 +27,11 @@ module.exports =
type:"messages-fetch"
window.chat.MessagePersistence.get station,start,end
sendMessage: (station,message,audience) ->
sendMessage: (message,audience) ->
serial = window.util.uuid32()
if station[0] isnt "~" then station = "~"+window.urb.ship+"/"+station
if audience.length is 0 then audience.push station
audience.push window.util.mainStationPath window.urb.user
audience = _.uniq audience
_audi = {}
for k,v of audience
@ -53,10 +50,14 @@ module.exports =
bouquet:[]
speech:
lin:
say:false
say:true
txt:message
date: Date.now()
if message[0] is "@"
_message.thought.statement.speech.lin.txt = _message.thought.statement.speech.lin.txt.slice(1).trim()
_message.thought.statement.speech.lin.say = false
MessageDispatcher.handleViewAction
type:"message-send"
message:_message

View File

@ -17,6 +17,11 @@ module.exports =
type:"station-set-audience"
audience:audience
setValidAudience: (valid) ->
StationDispatcher.handleViewAction
type:"station-set-valid-audience"
valid:valid
toggleAudience: (station) ->
StationDispatcher.handleViewAction
type:"station-audience-toggle"
@ -53,11 +58,10 @@ module.exports =
type:"stations-load"
stations:stations
loadMembers: (station,members) ->
loadMembers: (members) ->
StationDispatcher.handleServerAction
type:"members-load"
members:members
station:station
createStation: (station) ->
StationDispatcher.handleViewAction

View File

@ -1,13 +1,13 @@
moment = require 'moment-timezone'
recl = React.createClass
[div,input,textarea] = [React.DOM.div,React.DOM.input,React.DOM.textarea]
[div,br,input,textarea] = [React.DOM.div,React.DOM.br,React.DOM.input,React.DOM.textarea]
MessageStore = require '../stores/MessageStore.coffee'
StationStore = require '../stores/StationStore.coffee'
MessageActions = require '../actions/MessageActions.coffee'
MessageStore = require '../stores/MessageStore.coffee'
StationActions = require '../actions/StationActions.coffee'
Member = require './MemberComponent.coffee'
StationStore = require '../stores/StationStore.coffee'
Member = require './MemberComponent.coffee'
Message = recl
lz: (n) -> if n<10 then "0#{n}" else "#{n}"
@ -19,24 +19,31 @@ Message = recl
s = @lz d.getSeconds()
"~#{h}.#{m}.#{s}"
_handleAudi: (e) ->
audi = _.map $(e.target).closest('.audi').find('div'), (div) -> return $(div).text()
@props._handleAudi audi
_handlePm: (e) ->
return if not @props._handlePm
user = $(e.target).closest('.iden').text().slice(1)
@props._handlePm user
render: ->
# pendingClass = if @props.pending isnt "received" then "pending" else ""
delivery = _.uniq _.pluck @props.thought.audience, "delivery"
pendingClass = if delivery.indexOf("received") isnt -1 then "received" else "pending"
if pendingClass is "pending"
console.log @props.thought
console.log delivery
klass = if delivery.indexOf("received") isnt -1 then " received" else " pending"
if @props.thought.statement.speech.lin.say is false then klass += " say"
name = if @props.name then @props.name else ""
audi = _.remove _.keys(@props.thought.audience), (stat) =>
stat isnt "~"+window.urb.ship+"/"+@props.station
audi = audi.join " "
audi = _.keys @props.thought.audience
audi = _.without audi,window.util.mainStationPath window.urb.user
audi = window.util.clipAudi audi
audi = audi.map (_audi) -> (div {}, _audi)
div {className:"message "+pendingClass}, [
div {className:"message #{klass}"}, [
(div {className:"attr"}, [
(Member {ship:@props.ship}, "")
div {className:"audi"}, "#{audi}"
div {onClick:@_handleAudi,className:"audi"}, audi
(div {onClick:@_handlePm}, (Member {ship:@props.ship}, ""))
div {className:"time"}, @convTime @props.thought.statement.date
])
div {className:"mess"}, @props.thought.statement.speech.lin.txt
@ -51,7 +58,7 @@ module.exports = recl
last:MessageStore.getLast()
fetching:MessageStore.getFetching()
listening:MessageStore.getListening()
station:StationStore.getStation()
station:window.util.mainStation()
stations:StationStore.getStations()
configs:StationStore.getConfigs()
typing:MessageStore.getTyping()
@ -78,7 +85,8 @@ module.exports = recl
componentDidMount: ->
MessageStore.addChangeListener @_onChangeStore
StationStore.addChangeListener @_onChangeStore
if @state.station and @state.listening.indexOf(@state.station) is -1
if @state.station and
@state.listening.indexOf(@state.station) is -1
MessageActions.listenStation @state.station
checkMore = @checkMore
$(window).on 'scroll', checkMore
@ -101,14 +109,25 @@ module.exports = recl
_onChangeStore: -> @setState @stateFromStore()
_handlePm: (user) ->
audi = [
window.util.mainStationPath(user)
window.util.mainStationPath(window.urb.user)
]
if user is window.urb.user then audi.pop()
StationActions.setAudience audi
_handleAudi: (audi) -> StationActions.setAudience audi
render: ->
station = @state.station
_station = "~"+window.urb.ship+"/"+station
sources = _.clone @state.configs[@state.station]?.sources ? []
sources.push _station
_messages = _.filter @state.messages, (_message) ->
audience = _.keys(_message.thought.audience)
_.intersection(sources,audience).length > 0
_messages = @state.messages
# _messages = _.filter @state.messages, (_message) ->
# audience = _.keys(_message.thought.audience)
# _.intersection(sources,audience).length > 0
_messages = _.sortBy _messages, (_message) ->
_message.pending = _message.thought.audience[station]
_message.thought.statement.time
@ -122,5 +141,7 @@ module.exports = recl
messages = _messages.map (_message) =>
_message.station = @state.station
_message._handlePm = @_handlePm
_message._handleAudi = @_handleAudi
Message _message, ""
div {id: "messages"}, messages

View File

@ -1,15 +0,0 @@
recl = React.createClass
[div,input,textarea] = [React.DOM.div,React.DOM.input,React.DOM.textarea]
StationComponent = require './StationComponent.coffee'
MessagesComponent = require './MessagesComponent.coffee'
WritingComponent = require './WritingComponent.coffee'
module.exports = recl
render: ->
div {id:"d"}, "asdf"
# div {id:"d"}, [
# (div {id:'station-container'}, (StationComponent {}, ""))
# (div {id:'messages-container'}, (MessagesComponent {}, ""))
# (div {id:'writing-container'}, (WritingComponent {}, ""))
# ]

View File

@ -15,7 +15,7 @@ module.exports = recl
stateFromStore: -> {
audi:StationStore.getAudience()
members:StationStore.getMembers()
station:StationStore.getStation()
station:window.util.mainStation()
stations:StationStore.getStations()
configs:StationStore.getConfigs()
typing:StationStore.getTyping()
@ -35,10 +35,6 @@ module.exports = recl
componentWillUnmount: ->
StationStore.removeChangeListener @_onChangeStore
_toggleAudi: (e) ->
$e = $(e.target).closest('.station')
station = $e.find('.path').text()
StationActions.toggleAudience station
_onChangeStore: ->
@setState @stateFromStore()
@ -51,51 +47,50 @@ module.exports = recl
_sources.push v
StationActions.setSources @state.station,_sources
@$input.val('')
@$input.blur()
_remove: (e) ->
e.stopPropagation()
e.preventDefault()
_station = $(e.target).attr "data-station"
_sources = _.clone @state.configs[@state.station].sources
_sources.slice _sources.indexOf(_station),1
_sources.splice _sources.indexOf(_station),1
StationActions.setSources @state.station,_sources
render: ->
parts = []
members = []
if @state.station and @state.members[@state.station]
members = _.map @state.members[@state.station], (state,member) ->
Member {ship:member,presence:state.presence}
if @state.station and @state.members
members = _.map @state.members, (stations,member) ->
audi = _.map stations,(presence,station) -> (div {className:"audi"}, station)
(div {}, [audi,Member {ship:member}])
else
members = ""
sourceInput = [(input {className:"join",onKeyUp:@_keyUp,placeholder:"+"}, "")]
sourceCtrl = div {className:"sour-ctrl"}, sourceInput
sourceCtrl = div {className:"sour-ctrl"},sourceInput
sources = []
if @state.station and @state.configs[@state.station]
_remove = @_remove
_sources = _.clone @state.configs[@state.station].sources
_sources.push "twitter/hoontap"
sources = _.map _sources,(source) =>
toggleClass = "toggle "
if @state.audi.indexOf(source) isnt -1 then toggleClass += "active"
(div {className:"station",onClick:@_toggleAudi}, [
(div {className:toggleClass})
(div {className:"path"}, source),
(div {className:"remove",onClick:_remove,"data-station":source},"×")
(div {className:"station"}, [
(div {className:"remove",onClick:_remove,"data-station":source},"×"),
(div {className:"path"}, source)
])
else
sources = ""
station = []
station.push (a {className:"up",href:"\#/"}, [(div {className:"arow-up"}, "")])
station.push (h1 {},@state.station)
station.push (div {id:"members"},members)
head = (div {id:"head"},
[(div {id:"where"},["/talk",(div {className:"caret"},"")]),
(div {id:"who"},[(div {className:"circle"},""),"~#{window.urb.user}"])
]
)
parts.push (div {id:"station-container"}, (div {id:"station-meta"},station))
parts.push (div {id:"sources-container"}, [(div {class:"sources-list"},sources),sourceCtrl])
parts.push head
parts.push (div {id:"stations"}, [(h1 {}, "Sources"),(div {},sources),sourceCtrl])
parts.push (div {id:"audience"}, (div {},[(h1 {}, "Audience"),(div {id:"members"},members)]))
div {id:"station"},parts

View File

@ -7,15 +7,15 @@ StationActions = require '../actions/StationActions.coffee'
module.exports = recl
stateFromStore: -> {
stations: StationStore.getStations()
station: StationStore.getStation()
station: "~zod/court"
}
getInitialState: -> @stateFromStore()
componentDidMount: ->
@$el = $(@getDOMNode())
@$add = $('#stations .add')
@$input = @$el.find('input')
@$el = $ @getDOMNode()
@$add = $ '#stations .add'
@$input = @$el.find 'input'
StationStore.addChangeListener @_onChangeStore
componentWillUnmount: ->

View File

@ -1,10 +1,11 @@
recl = React.createClass
[div,input,textarea] = [React.DOM.div,React.DOM.input,React.DOM.textarea]
[div,br,input,textarea] = [React.DOM.div,React.DOM.br,React.DOM.input,React.DOM.textarea]
MessageActions = require '../actions/MessageActions.coffee'
MessageStore = require '../stores/MessageStore.coffee'
StationActions = require '../actions/StationActions.coffee'
StationStore = require '../stores/StationStore.coffee'
Member = require './MemberComponent.coffee'
StationStore = require '../stores/StationStore.coffee'
Member = require './MemberComponent.coffee'
module.exports = recl
set: ->
@ -13,12 +14,16 @@ module.exports = recl
get: ->
if window.localStorage then window.localStorage.getItem 'writing'
stateFromStore: -> {
audi:StationStore.getAudience()
members:StationStore.getMembers()
typing:StationStore.getTyping()
station:StationStore.getStation()
}
stateFromStore: ->
s =
audi:StationStore.getAudience()
ludi:MessageStore.getLastAudience()
members:StationStore.getMembers()
typing:StationStore.getTyping()
valid:StationStore.getValidAudience()
s.audi = _.without s.audi, window.util.mainStationPath window.urb.user
s.ludi = _.without s.ludi, window.util.mainStationPath window.urb.user
s
getInitialState: -> @stateFromStore()
@ -35,13 +40,30 @@ module.exports = recl
@typing true
sendMessage: ->
MessageActions.sendMessage @state.station,@$writing.text(),@state.audi
if @_validateAudi() is false
$('#audi').focus()
return
if @state.audi.length is 0 and $('#audi').text().trim().length > 0
audi = @state.ludi
@_setAudi()
else
audi = @state.audi
audi = window.util.expandAudi audi
MessageActions.sendMessage @$writing.text().trim(),audi
@$length.text "0/69"
@$writing.text('')
@set()
@typing false
_keyDown: (e) ->
_audiKeyDown: (e) ->
if e.keyCode is 13
e.preventDefault()
setTimeout () ->
$('#writing').focus()
,0
return false
_writingKeyDown: (e) ->
if e.keyCode is 13
e.preventDefault()
@sendMessage()
@ -67,6 +89,40 @@ module.exports = recl
_setFocus: -> @$writing.focus()
_validateAudiPart: (a) ->
if a[0] isnt "~"
return false
if a.indexOf("/") isnt -1
_a = a.split("/")
if _a[1].length is 0
return false
ship = _a[0]
else
ship = a
if ship.length < 3
return false
return true
_validateAudi: ->
v = $('#audi').text()
v = v.trim()
if v.length is 0
return true
v = v.split " "
for a in v
a = a.trim()
valid = @_validateAudiPart(a)
valid
_setAudi: ->
valid = @_validateAudi()
StationActions.setValidAudience valid
if valid is true
v = $('#audi').text()
v = v.split " "
v = window.util.expandAudi v
StationActions.setAudience v
getTime: ->
d = new Date()
seconds = d.getSeconds()
@ -85,6 +141,7 @@ module.exports = recl
componentDidMount: ->
window.util.sendMessage = @sendMessage
StationStore.addChangeListener @_onChangeStore
MessageStore.addChangeListener @_onChangeStore
@$el = $ @getDOMNode()
@$length = $('#length')
@$writing = $('#writing')
@ -108,11 +165,20 @@ module.exports = recl
ship = if iden then iden.ship else user
name = if iden then iden.name else ""
k = "writing"
k+= " hidden" if not @state?.station
audi = if @state.audi.length is 0 then @state.ludi else @state.audi
audi = window.util.clipAudi audi
div {className:k,onClick:@_setFocus}, [
k = "writing"
div {className:k}, [
(div {className:"attr"}, [
(div {
id:"audi"
className:"audi valid-#{@state.valid}"
contentEditable:true
onKeyDown: @_audiKeyDown
onBlur:@_setAudi
}, audi.join(" "))
(Member iden, "")
(div {className:"time"}, @getTime())
])
@ -123,7 +189,7 @@ module.exports = recl
onBlur: @_blur
onInput: @_input
onPaste: @_input
onKeyDown: @_keyDown
onKeyDown: @_writingKeyDown
onFocus: @cursorAtEnd
}, "")
div {id:"length"}, "0/69"

View File

@ -8,15 +8,33 @@ $(() ->
window.chat.StationPersistence = require './persistence/StationPersistence.coffee'
window.util =
mainStation: ->
switch window.urb.user.length
mainStations: ["court","floor","porch"]
mainStationPath: (user) -> "~#{user}/#{window.util.mainStation(user)}"
mainStation: (user) ->
if not user then user = window.urb.user
switch user.length
when 3
return "court"
when 5
when 6
return "floor"
when 13
return "porch"
clipAudi: (audi) ->
audi = audi.join " "
for v in window.util.mainStations
regx = new RegExp "/#{v}","g"
audi = audi.replace regx,""
audi.split " "
expandAudi: (audi) ->
for k,v of audi
if v.indexOf("/") is -1
audi[k] = "#{v}/#{window.util.mainStation(v.slice(1))}"
audi
create: (name) ->
window.chat.StationPersistence.createStation name, (err,res) ->
@ -54,8 +72,7 @@ $(() ->
send()
getScroll: ->
@writingPosition = $('#messaging-container').outerHeight(true)+$('#messaging-container').offset().top-$(window).height()
#@writingPosition = $('#writing-container').position().top-$(window).height()+$('#writing-container').outerHeight(true)
@writingPosition = $('#c').outerHeight(true)+$('#c').offset().top-$(window).height()
setScroll: ->
window.util.getScroll()
@ -69,43 +86,92 @@ $(() ->
else
$('body').removeClass 'scrolling'
# checkScroll = ->
# if $(window).scrollTop() > 20
# $('#nav').addClass 'scrolling'
# else
# $('#nav').removeClass 'scrolling'
# setInterval checkScroll, 500
so = {}
so.ls = $(window).scrollTop()
so.cs = $(window).scrollTop()
so.w = null
so.$n = $('#station-container')
so.$d = $('#nav > div')
so.nh = so.$n.outerHeight(true)
setSo = ->
so.$n = $('#station-container')
so.w = $(window).width()
setInterval setSo,200
$(window).on 'resize', (e) ->
if so.w > 1170
so.$n.removeClass 'm-up m-down m-fixed'
$(window).on 'scroll', (e) ->
so.cs = $(window).scrollTop()
if so.w > 1170
so.$n.removeClass 'm-up m-down m-fixed'
if so.w < 1170
dy = so.ls-so.cs
so.$d.removeClass 'focus'
if so.cs <= 0
so.$n.removeClass 'm-up'
so.$n.addClass 'm-down m-fixed'
return
if so.$n.hasClass 'm-fixed' and
so.w < 1024
so.$n.css left:-1*$(window).scrollLeft()
if dy > 0
if not so.$n.hasClass 'm-down'
so.$n.removeClass('m-up').addClass 'm-down'
top = so.cs-so.nh
if top < 0 then top = 0
so.$n.offset top:top
if so.$n.hasClass('m-down') and
not so.$n.hasClass('m-fixed') and
so.$n.offset().top >= so.cs
so.$n.addClass 'm-fixed'
so.$n.attr {style:''}
if dy < 0
if not so.$n.hasClass 'm-up'
so.$n.removeClass('m-down m-fixed').addClass 'm-up'
so.$n.attr {style:''}
top = so.cs
sto = so.$n.offset().top
if top < 0 then top = 0
if top > sto and top < sto+so.nh then top = sto
so.$n.offset top:top
so.ls = so.cs
$(window).on 'scroll', window.util.checkScroll
window.chat.StationPersistence.listen()
StationComponent = require './components/StationComponent.coffee'
StationsComponent = require './components/StationsComponent.coffee'
MessagesComponent = require './components/MessagesComponent.coffee'
WritingComponent = require './components/WritingComponent.coffee'
$c = $('#c')
clean = ->
React.unmountComponentAtNode $('#stations-container')[0]
React.unmountComponentAtNode $('#station-parts-container')[0]
React.unmountComponentAtNode $('#writing-container')[0]
React.unmountComponentAtNode $('#station-container')[0]
React.unmountComponentAtNode $('#messages-container')[0]
React.unmountComponentAtNode $('#writing-container')[0]
routes =
'': ->
clean()
$c.html "<div id='stations-container'></div>"
rend (StationsComponent {}, ""),$('#stations-container')[0]
'/:station': (station) ->
clean()
StationActions.switchStation station
$c.html ""
$c.append("<div id='messaging-container'></div>")
$d = $('#messaging-container')
$d.append("<div id='messages-container'></div>")
$d.append("<div id='writing-container'></div>")
$d.append("<div id='station-parts-container'></div>")
$c.append("<div id='scrolling'>BOTTOM</div>")
rend (StationComponent {}, ""),$('#station-parts-container')[0]
rend (MessagesComponent {}, ""),$('#messages-container')[0]
rend (WritingComponent {}, ""),$('#writing-container')[0]
router = Router routes
if not window.location.hash then window.location.hash = "/"
router.init()
$c.append "<div id='station-container'></div>"
$c.append "<div id='messages-container'></div>"
$c.append "<div id='writing-container'></div>"
$c.append "<div id='scrolling'>BOTTOM</div>"
rend (StationComponent {}, ""),$('#station-container')[0]
rend (MessagesComponent {}, ""),$('#messages-container')[0]
rend (WritingComponent {}, ""),$('#writing-container')[0]
)

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,10 @@
{
"name": "urbit-radio",
"version": "0.0.0",
"repository": {
"type":"git",
"url":"https://github.com/urbit/urbit"
},
"description": "urbit radio frontend",
"main": "main.js",
"dependencies": {

View File

@ -3,7 +3,7 @@ MessageActions = require '../actions/MessageActions.coffee'
module.exports =
listenStation: (station,since) ->
window.urb.subscribe {
appl:"rodeo"
appl:"radio"
path:"/f/#{station}/#{since}"
}, (err,res) ->
console.log('m subscription updates')
@ -15,7 +15,7 @@ module.exports =
get: (station,start,end) ->
window.urb.subscribe {
appl:"rodeo"
appl:"radio"
path:"/f/#{station}/#{end}/#{start}"
}, (err,res) ->
console.log 'get'
@ -23,7 +23,7 @@ module.exports =
if res.data?.grams?.tele
MessageActions.loadMessages res.data.grams,true
window.urb.unsubscribe {
appl:"rodeo"
appl:"radio"
path:"/f/#{station}/#{end}/#{start}"
}, (err,res) ->
console.log 'done'
@ -31,8 +31,8 @@ module.exports =
sendMessage: (message,cb) ->
window.urb.send {
appl:"rodeo"
mark:"rodeo-command"
appl:"radio"
mark:"radio-command"
data:
publish: [
message

View File

@ -3,8 +3,8 @@ StationActions = require '../actions/StationActions.coffee'
module.exports =
createStation: (name,cb) ->
window.urb.send {
appl:"rodeo"
mark:"rodeo-command"
appl:"radio"
mark:"radio-command"
data:
design:
party:name
@ -16,8 +16,8 @@ module.exports =
removeStation: (name,cb) ->
window.urb.send {
appl:"rodeo"
mark:"rodeo-command"
appl:"radio"
mark:"radio-command"
data:
design:
party:name
@ -26,8 +26,8 @@ module.exports =
setSources: (station,ship,sources) ->
send =
appl:"rodeo"
mark:"rodeo-command"
appl:"radio"
mark:"radio-command"
data:
design:
party:station
@ -41,7 +41,7 @@ module.exports =
members: ->
window.urb.subscribe {
appl:"rodeo"
appl:"radio"
path:"/a/court"
}, (err,res) ->
console.log 'membership updates'
@ -51,7 +51,7 @@ module.exports =
listen: ->
window.urb.subscribe {
appl:"rodeo"
appl:"radio"
path:"/"
}, (err,res) ->
console.log 'house updates'
@ -61,14 +61,15 @@ module.exports =
listenStation: (station) ->
window.urb.subscribe {
appl:"rodeo"
appl:"radio"
path:"/ax/#{station}"
}, (err,res) ->
console.log('station subscription updates')
console.log(res.data)
if res.data.ok is true
StationActions.listeningStation station
if res.data.group?.local
StationActions.loadMembers station,res.data.group.local
if res.data.group
res.data.group.global[window.util.mainStationPath(window.urb.user)] = res.data.group.local
StationActions.loadMembers res.data.group.global
if res.data.config
StationActions.loadConfig station,res.data.config

View File

@ -33,6 +33,11 @@ MessageStore = _.merge new EventEmitter,{
getTyping: -> _typing
getLastAudience: ->
if _.keys(_messages).length is 0 then return []
messages = _.sortBy _messages, (_message) -> _message.thought.statement.time
_.keys messages[messages.length-1].thought.audience
setTyping: (state) -> _typing = state
setListening: (station) ->

View File

@ -1,14 +1,16 @@
EventEmitter = require('events').EventEmitter
EventEmitter = require('events').EventEmitter
StationDispatcher = require '../dispatcher/Dispatcher.coffee'
_audience = []
_members = {}
_stations = []
_listening = []
_station = null
_config = {}
_typing = {}
_audience = []
_members = {}
_stations = []
_listening = []
_station = null
_config = {}
_typing = {}
_validAudience = true
StationStore = _.merge new EventEmitter,{
removeChangeListener: (cb) -> @removeListener "change", cb
@ -21,6 +23,10 @@ StationStore = _.merge new EventEmitter,{
setAudience: (audience) -> _audience = audience
getValidAudience: -> _validAudience
setValidAudience: (valid) -> _validAudience = valid
toggleAudience: (station) ->
if _audience.indexOf(station) isnt -1
_audience.splice _audience.indexOf(station), 1
@ -35,14 +41,12 @@ StationStore = _.merge new EventEmitter,{
getMember: (ship) -> {ship:ship}
changeMember: (dir,name,ship) ->
if dir is "out"
_members = _.filter _members, (_member) ->
return (_member.ship isnt ship)
if dir is "in"
_members.push {name:name, ship:ship}
loadMembers: (station,members) -> _members[station] = members
loadMembers: (members) ->
_members = {}
for station,list of members
for member,presence of list
_members[member] = {} if not _members[member]
_members[member][station] = presence
getMembers: -> _members
@ -94,6 +98,10 @@ StationStore.dispatchToken = StationDispatcher.register (payload) ->
StationStore.setAudience action.audience
StationStore.emitChange()
break
when 'station-set-valid-audience'
StationStore.setValidAudience action.valid
StationStore.emitChange()
break
when 'station-switch'
StationStore.setAudience []
StationStore.setStation action.station
@ -103,7 +111,7 @@ StationStore.dispatchToken = StationDispatcher.register (payload) ->
StationStore.setListening action.station
StationStore.emitChange()
break
when "config-load"
when "config-load" #[name:'loadConfig', args:['station', 'config']]
StationStore.loadConfig action.station,action.config
StationStore.emitChange()
break
@ -111,7 +119,10 @@ StationStore.dispatchToken = StationDispatcher.register (payload) ->
StationStore.loadStations action.stations
StationStore.emitChange()
break
when "stations-leave"
when "stations-leave" # stations-leave:[{name:'loadStations' args:['stations']} ['unsetStation' 'station']]
# ...
# for command in actionVtable[action.type]
# StationStore[command[0]].apply(command[1..].map(argname -> action[argname]))
StationStore.loadStations action.stations
StationStore.unsetStation action.station
StationStore.emitChange()
@ -121,7 +132,7 @@ StationStore.dispatchToken = StationDispatcher.register (payload) ->
StationStore.emitChange()
break
when "members-load"
StationStore.loadMembers action.station,action.members
StationStore.loadMembers action.members
StationStore.emitChange()
break
when "typing-set"