mirror of
https://github.com/urbit/shrub.git
synced 2024-12-13 16:03:36 +03:00
talk in
This commit is contained in:
parent
d10ee595fa
commit
c1fcc47513
@ -9,19 +9,15 @@
|
||||
;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 "/gen/main/pub/talk/src/dep/director.js");
|
||||
;script(type "text/javascript", src "/gen/main/pub/talk/src/js/dep/director.js");
|
||||
;meta(name "viewport", content "width=432, initial-scale=1");
|
||||
;script(type "text/javascript", src "//use.typekit.net/fkv0sjk.js");
|
||||
;script:'''
|
||||
try{Typekit.load();}catch(e){}
|
||||
'''
|
||||
;link(type "text/css", rel "stylesheet", href "/gen/main/pub/talk/src/main.css");
|
||||
;link(type "text/css", rel "stylesheet", href "/gen/main/pub/talk/src/css/main.css");
|
||||
;script(type "text/javascript", src "/gop/hart.js");
|
||||
;title: Radio
|
||||
==
|
||||
;body
|
||||
;div#c;
|
||||
;script(type "text/javascript", src "/gen/main/lib/urb.js");
|
||||
;script(type "text/javascript", src "/gen/main/pub/talk/src/main.js");
|
||||
;script(type "text/javascript", src "/gen/main/pub/talk/src/js/main.js");
|
||||
==
|
||||
==
|
||||
|
72
main/pub/talk/src/css/fonts.styl
Normal file
72
main/pub/talk/src/css/fonts.styl
Normal file
@ -0,0 +1,72 @@
|
||||
@font-face {
|
||||
font-family: "bau";
|
||||
src: url("http://storage.googleapis.com/urbit-extra/bau.woff");
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "bau";
|
||||
src: url("http://storage.googleapis.com/urbit-extra/bau-italic.woff");
|
||||
font-weight: 400;
|
||||
font-style: italic;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "bau";
|
||||
src: url("http://storage.googleapis.com/urbit-extra/bau-medium.woff");
|
||||
font-weight: 500;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "bau";
|
||||
src: url("http://storage.googleapis.com/urbit-extra/bau-mediumitalic.woff");
|
||||
font-weight: 500;
|
||||
font-style: italic;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "bau";
|
||||
src: url("http://storage.googleapis.com/urbit-extra/bau-bold.woff");
|
||||
font-weight: 600;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "bau";
|
||||
src: url("http://storage.googleapis.com/urbit-extra/bau-bolditalic.woff");
|
||||
font-weight: 600;
|
||||
font-style: italic;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "bau";
|
||||
src: url("http://storage.googleapis.com/urbit-extra/bau-super.woff");
|
||||
font-weight: 600;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "bau";
|
||||
src: url("http://storage.googleapis.com/urbit-extra/bau-superitalic.woff");
|
||||
font-weight: 600;
|
||||
font-style: italic;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "scp";
|
||||
src: url("http://storage.googleapis.com/urbit-extra/scp-extralight.woff");
|
||||
font-weight: 200;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "scp";
|
||||
src: url("http://storage.googleapis.com/urbit-extra/scp-light.woff");
|
||||
font-weight: 300;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "scp";
|
||||
src: url("http://storage.googleapis.com/urbit-extra/scp-regular.woff");
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "scp";
|
||||
src: url("http://storage.googleapis.com/urbit-extra/scp-medium.woff");
|
||||
font-weight: 500;
|
||||
font-style: normal;
|
||||
}
|
@ -1,44 +1,80 @@
|
||||
@font-face {
|
||||
font-family: "bau";
|
||||
src: url("http://office-for.com/lib/etc/bau/bauregular.otf");
|
||||
src: url("http://storage.googleapis.com/urbit-extra/bau.woff");
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "bau";
|
||||
src: url("http://office-for.com/lib/etc/bau/bauregularitalic.otf");
|
||||
src: url("http://storage.googleapis.com/urbit-extra/bau-italic.woff");
|
||||
font-weight: 400;
|
||||
font-style: italic;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "bau";
|
||||
src: url("http://office-for.com/lib/etc/bau/baumedium.otf");
|
||||
src: url("http://storage.googleapis.com/urbit-extra/bau-medium.woff");
|
||||
font-weight: 500;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "bau";
|
||||
src: url("http://office-for.com/lib/etc/bau/baumediumitalic.otf");
|
||||
src: url("http://storage.googleapis.com/urbit-extra/bau-mediumitalic.woff");
|
||||
font-weight: 500;
|
||||
font-style: italic;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "bau";
|
||||
src: url("http://office-for.com/lib/etc/bau/baubold.otf");
|
||||
src: url("http://storage.googleapis.com/urbit-extra/bau-bold.woff");
|
||||
font-weight: 600;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "bau";
|
||||
src: url("http://office-for.com/lib/etc/bau/baubolditalic.otf");
|
||||
src: url("http://storage.googleapis.com/urbit-extra/bau-bolditalic.woff");
|
||||
font-weight: 600;
|
||||
font-style: italic;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "bau";
|
||||
src: url("http://storage.googleapis.com/urbit-extra/bau-super.woff");
|
||||
font-weight: 600;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "bau";
|
||||
src: url("http://storage.googleapis.com/urbit-extra/bau-superitalic.woff");
|
||||
font-weight: 600;
|
||||
font-style: italic;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "scp";
|
||||
src: url("http://storage.googleapis.com/urbit-extra/scp-extralight.woff");
|
||||
font-weight: 200;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "scp";
|
||||
src: url("http://storage.googleapis.com/urbit-extra/scp-light.woff");
|
||||
font-weight: 300;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "scp";
|
||||
src: url("http://storage.googleapis.com/urbit-extra/scp-regular.woff");
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "scp";
|
||||
src: url("http://storage.googleapis.com/urbit-extra/scp-medium.woff");
|
||||
font-weight: 500;
|
||||
font-style: normal;
|
||||
}
|
||||
.iden,
|
||||
.audi,
|
||||
.time,
|
||||
#length {
|
||||
font-family: "source-code-pro";
|
||||
font-family: "scp";
|
||||
}
|
||||
.join-ctrl input {
|
||||
font-family: "bau";
|
||||
@ -93,11 +129,11 @@ body {
|
||||
top: 0;
|
||||
left: 50%;
|
||||
width: 28rem;
|
||||
max-height: 2.9rem;
|
||||
max-height: 2.6rem;
|
||||
overflow: hidden;
|
||||
margin-left: -14rem;
|
||||
padding-top: 1rem;
|
||||
background-color: #fff;
|
||||
background-color: #f5f5f5;
|
||||
border-bottom: 3px solid #ededed;
|
||||
transition: max-height 0.15s ease-out;
|
||||
}
|
||||
@ -119,7 +155,6 @@ body {
|
||||
}
|
||||
#station > div {
|
||||
display: inline-block;
|
||||
float: left;
|
||||
}
|
||||
#station-meta {
|
||||
margin-right: 1rem;
|
||||
@ -127,6 +162,8 @@ body {
|
||||
}
|
||||
#sources-container {
|
||||
width: 6rem;
|
||||
float: right;
|
||||
margin: 1rem -6rem 0 0;
|
||||
}
|
||||
#members {
|
||||
margin-left: 2rem;
|
||||
@ -146,10 +183,12 @@ body {
|
||||
width: 24rem;
|
||||
}
|
||||
.station .name {
|
||||
padding: 0.3rem;
|
||||
border-bottom: 6px solid transparent;
|
||||
}
|
||||
.station .name:hover {
|
||||
border-bottom: 6px solid #000;
|
||||
background-color: #f5f5f5;
|
||||
border-bottom: 6px solid #ededed;
|
||||
}
|
||||
.station > div {
|
||||
display: inline-block;
|
||||
@ -174,7 +213,7 @@ body {
|
||||
display: inline;
|
||||
}
|
||||
#sources-container .station {
|
||||
font-family: "source-code-pro";
|
||||
font-family: "scp";
|
||||
font-weight: 500;
|
||||
text-transform: lowercase;
|
||||
}
|
||||
@ -195,7 +234,6 @@ body {
|
||||
}
|
||||
.message {
|
||||
padding-top: 0.3rem;
|
||||
margin-bottom: 0.6rem;
|
||||
}
|
||||
#messages .message:last-child {
|
||||
margin-bottom: 1rem;
|
||||
@ -232,8 +270,11 @@ body {
|
||||
#length {
|
||||
vertical-align: top;
|
||||
}
|
||||
.attr {
|
||||
color: #d7d7d7;
|
||||
}
|
||||
.attr .iden {
|
||||
margin-left: 1rem;
|
||||
color: #000;
|
||||
}
|
||||
.attr > div {
|
||||
margin-right: 0.3rem;
|
||||
@ -262,6 +303,7 @@ body {
|
||||
#writing-container {
|
||||
bottom: 4rem;
|
||||
margin-bottom: 1rem;
|
||||
float: left;
|
||||
}
|
||||
.writing {
|
||||
padding-top: 0.3rem;
|
||||
@ -273,11 +315,12 @@ body {
|
||||
}
|
||||
#writing {
|
||||
font-size: 0.9rem;
|
||||
min-height: 1.3rem;
|
||||
min-height: 1.6rem;
|
||||
line-height: 1.6rem;
|
||||
min-width: 1.3rem;
|
||||
padding: 0;
|
||||
outline: none;
|
||||
background-color: #20c567;
|
||||
background-color: #eee;
|
||||
}
|
||||
#writing:focus {
|
||||
background-color: #fff;
|
||||
@ -296,7 +339,7 @@ input.join {
|
||||
width: 24rem;
|
||||
}
|
||||
#station input.join {
|
||||
font-family: "source-code-pro";
|
||||
font-family: "scp";
|
||||
font-size: 0.7rem;
|
||||
line-height: 1rem;
|
||||
width: 12rem;
|
||||
@ -318,12 +361,12 @@ a.up {
|
||||
}
|
||||
.arow-up {
|
||||
display: inline-block;
|
||||
margin-right: 1rem;
|
||||
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 #ff6b00;
|
||||
border-bottom: 9px solid #000;
|
||||
}
|
||||
#scrolling {
|
||||
display: none;
|
379
main/pub/talk/src/css/main.styl
Normal file
379
main/pub/talk/src/css/main.styl
Normal file
@ -0,0 +1,379 @@
|
||||
@import 'fonts'
|
||||
|
||||
.iden
|
||||
.audi
|
||||
.time
|
||||
#length
|
||||
font-family "scp"
|
||||
|
||||
.join-ctrl input
|
||||
font-family "bau"
|
||||
|
||||
input
|
||||
-webkit-appearance none
|
||||
border-radius 0
|
||||
|
||||
html
|
||||
body
|
||||
height 100%
|
||||
font-family "bau"
|
||||
|
||||
#length
|
||||
.time
|
||||
font-size .6rem
|
||||
letter-spacing 0
|
||||
font-weight 200
|
||||
|
||||
input.join
|
||||
.iden
|
||||
.audi
|
||||
#station
|
||||
font-size .7rem
|
||||
|
||||
html
|
||||
body
|
||||
font-size 18px
|
||||
|
||||
body
|
||||
background-color #fefefe
|
||||
padding 0
|
||||
margin 0
|
||||
|
||||
.hidden
|
||||
display none
|
||||
|
||||
#c
|
||||
top 0
|
||||
background-color #fff
|
||||
|
||||
#stations-container
|
||||
position absolute
|
||||
top 1rem
|
||||
left 50%
|
||||
width 24rem
|
||||
margin-left -12rem
|
||||
font-size 4rem
|
||||
|
||||
#station-container
|
||||
position fixed
|
||||
top 0
|
||||
left 50%
|
||||
width 28rem
|
||||
max-height 2.6rem
|
||||
overflow hidden
|
||||
margin-left -14rem
|
||||
padding-top 1rem
|
||||
background-color #f5f5f5
|
||||
border-bottom 3px solid #ededed
|
||||
transition max-height 0.15s ease-out
|
||||
|
||||
#station-container:hover
|
||||
max-height 12rem
|
||||
transition max-height 0.25s ease-in
|
||||
|
||||
#stations-container
|
||||
#messages-container
|
||||
vertical-align top
|
||||
|
||||
#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
|
||||
margin-bottom 1rem
|
||||
|
||||
#sources-container
|
||||
width 6rem
|
||||
float right
|
||||
margin 1rem -6rem 0 0
|
||||
|
||||
#members
|
||||
margin-left 2rem
|
||||
|
||||
#station .iden
|
||||
display block
|
||||
|
||||
.station
|
||||
display inline-block
|
||||
width 9rem
|
||||
margin-bottom .3rem
|
||||
cursor pointer
|
||||
font-weight 200
|
||||
|
||||
#stations .station
|
||||
display block
|
||||
width 24rem
|
||||
|
||||
.station .name
|
||||
padding .3rem
|
||||
border-bottom 6px solid transparent
|
||||
|
||||
.station .name:hover
|
||||
background-color #f5f5f5
|
||||
border-bottom 6px solid #ededed
|
||||
|
||||
.station > div
|
||||
display inline-block
|
||||
|
||||
.toggle
|
||||
width .4rem
|
||||
height .4rem
|
||||
border 2px solid #000
|
||||
margin-right .6rem
|
||||
|
||||
.toggle.active
|
||||
background-color #000
|
||||
|
||||
.station .remove
|
||||
display none
|
||||
float right
|
||||
margin-left 1rem
|
||||
font-weight 600
|
||||
color #ff0000
|
||||
|
||||
.station:hover .remove
|
||||
display inline
|
||||
|
||||
#sources-container .station
|
||||
font-family "scp"
|
||||
font-weight 500
|
||||
text-transform lowercase
|
||||
|
||||
// hate this.
|
||||
.sour-ctrl
|
||||
margin-bottom .16rem
|
||||
|
||||
.join-ctrl input
|
||||
.sour-ctrl input
|
||||
border none
|
||||
font-weight 400
|
||||
|
||||
.sour-ctrl input::-webkit-input-placeholder
|
||||
font-family "bau"
|
||||
font-size 1rem
|
||||
font-weight 200
|
||||
margin-left .6rem
|
||||
color #0003FF
|
||||
|
||||
.message
|
||||
padding-top .3rem
|
||||
|
||||
#messages .message:last-child
|
||||
margin-bottom 1rem
|
||||
|
||||
#messages .message .time
|
||||
opacity 0
|
||||
|
||||
#messages .message:last-child .time
|
||||
#messages .message:hover .time
|
||||
opacity 1
|
||||
|
||||
.time
|
||||
margin-right .6rem
|
||||
|
||||
.member
|
||||
width 12rem
|
||||
margin .3rem .6rem .3rem 0
|
||||
|
||||
#messages
|
||||
height auto
|
||||
|
||||
.mess
|
||||
.iden
|
||||
.attr > div
|
||||
#station .member div
|
||||
#writing
|
||||
display inline-block
|
||||
|
||||
.iden > div
|
||||
display inline
|
||||
|
||||
.mess
|
||||
#writing
|
||||
#length
|
||||
vertical-align top
|
||||
|
||||
.attr
|
||||
color #D7D7D7
|
||||
|
||||
.attr .iden
|
||||
color #000
|
||||
|
||||
.attr > div
|
||||
margin-right .3rem
|
||||
|
||||
.mess
|
||||
font-size .9rem
|
||||
line-height 1.6rem
|
||||
letter-spacing .03rem
|
||||
word-wrap break-word
|
||||
max-width 31rem
|
||||
|
||||
.ship
|
||||
font-weight 600
|
||||
|
||||
.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-container
|
||||
bottom 4rem
|
||||
margin-bottom 1rem
|
||||
float left
|
||||
|
||||
.writing
|
||||
padding-top .3rem
|
||||
|
||||
.writing #length
|
||||
display inline
|
||||
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
|
||||
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
|
||||
outline none
|
||||
width 24rem
|
||||
|
||||
#station input.join
|
||||
font-family "scp"
|
||||
font-size .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 .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
|
||||
display none
|
||||
|
||||
.scrolling #scrolling
|
||||
position fixed
|
||||
bottom 3rem
|
||||
left 2rem
|
||||
height 1rem
|
||||
padding 1rem
|
||||
height 2rem
|
||||
background-color #f9f9f9
|
||||
font-weight 500
|
||||
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
|
||||
|
63
main/pub/talk/src/js/actions/MessageActions.coffee
Normal file
63
main/pub/talk/src/js/actions/MessageActions.coffee
Normal file
@ -0,0 +1,63 @@
|
||||
MessageDispatcher = require '../dispatcher/Dispatcher.coffee'
|
||||
|
||||
# hm
|
||||
|
||||
module.exports =
|
||||
loadMessages: (grams,get) ->
|
||||
MessageDispatcher.handleServerAction
|
||||
type:"messages-load"
|
||||
messages:grams.tele
|
||||
last:grams.num
|
||||
get:get
|
||||
|
||||
listenStation: (station,date) ->
|
||||
if not date then date = window.urb.util.toDate(new Date())
|
||||
window.chat.MessagePersistence.listenStation station,date
|
||||
|
||||
listeningStation: (station) ->
|
||||
MessageDispatcher.handleViewAction
|
||||
type:"messages-listen"
|
||||
station:station
|
||||
|
||||
setTyping: (state) ->
|
||||
MessageDispatcher.handleViewAction
|
||||
type:"messages-typing"
|
||||
state:state
|
||||
|
||||
getMore: (station,start,end) ->
|
||||
MessageDispatcher.handleViewAction
|
||||
type:"messages-fetch"
|
||||
window.chat.MessagePersistence.get station,start,end
|
||||
|
||||
sendMessage: (station,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
|
||||
|
||||
_audi = {}
|
||||
for k,v of audience
|
||||
_audi[v] =
|
||||
envelope:
|
||||
visible:true
|
||||
sender:null
|
||||
delivery:"pending"
|
||||
|
||||
_message =
|
||||
ship:window.urb.ship
|
||||
thought:
|
||||
serial:serial
|
||||
audience:_audi
|
||||
statement:
|
||||
bouquet:[]
|
||||
speech:
|
||||
lin:
|
||||
say:false
|
||||
txt:message
|
||||
date: Date.now()
|
||||
|
||||
MessageDispatcher.handleViewAction
|
||||
type:"message-send"
|
||||
message:_message
|
||||
window.chat.MessagePersistence.sendMessage _message.thought
|
66
main/pub/talk/src/js/actions/StationActions.coffee
Normal file
66
main/pub/talk/src/js/actions/StationActions.coffee
Normal file
@ -0,0 +1,66 @@
|
||||
StationDispatcher = require '../dispatcher/Dispatcher.coffee'
|
||||
|
||||
module.exports =
|
||||
loadConfig: (station,config) ->
|
||||
StationDispatcher.handleServerAction
|
||||
type: "config-load"
|
||||
station:station
|
||||
config:config
|
||||
|
||||
switchStation: (station) ->
|
||||
StationDispatcher.handleViewAction
|
||||
type:"station-switch"
|
||||
station:station
|
||||
|
||||
setAudience: (audience) ->
|
||||
StationDispatcher.handleViewAction
|
||||
type:"station-set-audience"
|
||||
audience:audience
|
||||
|
||||
toggleAudience: (station) ->
|
||||
StationDispatcher.handleViewAction
|
||||
type:"station-audience-toggle"
|
||||
station:station
|
||||
|
||||
removeStation: (station) ->
|
||||
window.chat.StationPersistence.removeStation station
|
||||
|
||||
setSources: (station,sources) ->
|
||||
window.chat.StationPersistence.setSources station,window.urb.ship,sources
|
||||
|
||||
createStation: (name) ->
|
||||
window.chat.StationPersistence.createStation name
|
||||
|
||||
listenStation: (station) ->
|
||||
window.chat.StationPersistence.listenStation station
|
||||
|
||||
listeningStation: (station) ->
|
||||
StationDispatcher.handleViewAction
|
||||
type:"station-listen"
|
||||
station:station
|
||||
|
||||
setTyping: (station,state) ->
|
||||
StationDispatcher.handleViewAction
|
||||
type:"typing-set"
|
||||
station:station
|
||||
state:state
|
||||
|
||||
ping: (_ping) ->
|
||||
window.chat.StationPersistence.ping _ping
|
||||
|
||||
loadStations: (stations) ->
|
||||
StationDispatcher.handleServerAction
|
||||
type:"stations-load"
|
||||
stations:stations
|
||||
|
||||
loadMembers: (station,members) ->
|
||||
StationDispatcher.handleServerAction
|
||||
type:"members-load"
|
||||
members:members
|
||||
station:station
|
||||
|
||||
createStation: (station) ->
|
||||
StationDispatcher.handleViewAction
|
||||
type: "station-create"
|
||||
station: station
|
||||
window.chat.StationPersistence.createStation station
|
11
main/pub/talk/src/js/components/MemberComponent.coffee
Normal file
11
main/pub/talk/src/js/components/MemberComponent.coffee
Normal file
@ -0,0 +1,11 @@
|
||||
recl = React.createClass
|
||||
[div,input,textarea] = [React.DOM.div,React.DOM.input,React.DOM.textarea]
|
||||
|
||||
module.exports = recl
|
||||
render: ->
|
||||
if @props.ship[0] isnt "~" then @props.ship = "~"+@props.ship
|
||||
k = "ship"
|
||||
k+= " #{@props.presence}" if @props.presence
|
||||
div {className:"iden"}, [
|
||||
div {className:k}, @props.ship
|
||||
]
|
126
main/pub/talk/src/js/components/MessagesComponent.coffee
Normal file
126
main/pub/talk/src/js/components/MessagesComponent.coffee
Normal file
@ -0,0 +1,126 @@
|
||||
moment = require 'moment-timezone'
|
||||
|
||||
recl = React.createClass
|
||||
[div,input,textarea] = [React.DOM.div,React.DOM.input,React.DOM.textarea]
|
||||
|
||||
MessageStore = require '../stores/MessageStore.coffee'
|
||||
StationStore = require '../stores/StationStore.coffee'
|
||||
MessageActions = require '../actions/MessageActions.coffee'
|
||||
StationActions = require '../actions/StationActions.coffee'
|
||||
Member = require './MemberComponent.coffee'
|
||||
|
||||
Message = recl
|
||||
lz: (n) -> if n<10 then "0#{n}" else "#{n}"
|
||||
|
||||
convTime: (time) ->
|
||||
d = new Date time
|
||||
h = @lz d.getHours()
|
||||
m = @lz d.getMinutes()
|
||||
s = @lz d.getSeconds()
|
||||
"~#{h}.#{m}.#{s}"
|
||||
|
||||
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
|
||||
|
||||
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 " "
|
||||
|
||||
div {className:"message "+pendingClass}, [
|
||||
(div {className:"attr"}, [
|
||||
(Member {ship:@props.ship}, "")
|
||||
div {className:"audi"}, "#{audi}"
|
||||
div {className:"time"}, @convTime @props.thought.statement.date
|
||||
])
|
||||
div {className:"mess"}, @props.thought.statement.speech.lin.txt
|
||||
]
|
||||
|
||||
module.exports = recl
|
||||
pageSize: 50
|
||||
paddingTop: 100
|
||||
|
||||
stateFromStore: -> {
|
||||
messages:MessageStore.getAll()
|
||||
last:MessageStore.getLast()
|
||||
fetching:MessageStore.getFetching()
|
||||
listening:MessageStore.getListening()
|
||||
station:StationStore.getStation()
|
||||
stations:StationStore.getStations()
|
||||
configs:StationStore.getConfigs()
|
||||
typing:MessageStore.getTyping()
|
||||
}
|
||||
|
||||
getInitialState: -> @stateFromStore()
|
||||
|
||||
checkMore: ->
|
||||
if $(window).scrollTop() < @paddingTop &&
|
||||
@state.fetching is false &&
|
||||
this.state.last &&
|
||||
this.state.last > 0
|
||||
end = @state.last-@pageSize
|
||||
end = 0 if end < 0
|
||||
@lastLength = @length
|
||||
MessageActions.getMore @state.station,(@state.last+1),end
|
||||
|
||||
setAudience: ->
|
||||
return if not @last
|
||||
if _.keys(@last.thought.audience).length > 0 and @state.typing is false and
|
||||
_.difference(_.keys(@last.thought.audience),@state.audi).length is 0
|
||||
StationActions.setAudience _.keys(@last.thought.audience)
|
||||
|
||||
componentDidMount: ->
|
||||
MessageStore.addChangeListener @_onChangeStore
|
||||
StationStore.addChangeListener @_onChangeStore
|
||||
if @state.station and @state.listening.indexOf(@state.station) is -1
|
||||
MessageActions.listenStation @state.station
|
||||
checkMore = @checkMore
|
||||
$(window).on 'scroll', checkMore
|
||||
window.util.setScroll()
|
||||
|
||||
componentDidUpdate: ->
|
||||
$window = $(window)
|
||||
if @lastLength
|
||||
h = $('.message').height() * (@length-@lastLength)
|
||||
st = $window.scrollTop()
|
||||
$window.scrollTop st+h
|
||||
@lastLength = null
|
||||
else
|
||||
if $('#writing-container').length > 0
|
||||
window.util.setScroll()
|
||||
|
||||
componentWillUnmount: ->
|
||||
MessageStore.removeChangeListener @_onChangeStore
|
||||
StationStore.removeChangeListener @_onChangeStore
|
||||
|
||||
_onChangeStore: -> @setState @stateFromStore()
|
||||
|
||||
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 = _.sortBy _messages, (_message) ->
|
||||
_message.pending = _message.thought.audience[station]
|
||||
_message.thought.statement.time
|
||||
|
||||
@last = _messages[_messages.length-1]
|
||||
@length = _messages.length
|
||||
|
||||
setTimeout =>
|
||||
@checkMore() if length < @pageSize
|
||||
, 1
|
||||
|
||||
messages = _messages.map (_message) =>
|
||||
_message.station = @state.station
|
||||
Message _message, ""
|
||||
div {id: "messages"}, messages
|
15
main/pub/talk/src/js/components/PageComponent.coffee
Normal file
15
main/pub/talk/src/js/components/PageComponent.coffee
Normal file
@ -0,0 +1,15 @@
|
||||
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 {}, ""))
|
||||
# ]
|
101
main/pub/talk/src/js/components/StationComponent.coffee
Normal file
101
main/pub/talk/src/js/components/StationComponent.coffee
Normal file
@ -0,0 +1,101 @@
|
||||
recl = React.createClass
|
||||
[div,input,textarea,h1,a] = [
|
||||
React.DOM.div,
|
||||
React.DOM.input,
|
||||
React.DOM.textarea,
|
||||
React.DOM.h1,
|
||||
React.DOM.a
|
||||
]
|
||||
|
||||
StationStore = require '../stores/StationStore.coffee'
|
||||
StationActions = require '../actions/StationActions.coffee'
|
||||
Member = require './MemberComponent.coffee'
|
||||
|
||||
module.exports = recl
|
||||
stateFromStore: -> {
|
||||
audi:StationStore.getAudience()
|
||||
members:StationStore.getMembers()
|
||||
station:StationStore.getStation()
|
||||
stations:StationStore.getStations()
|
||||
configs:StationStore.getConfigs()
|
||||
typing:StationStore.getTyping()
|
||||
listening:StationStore.getListening()
|
||||
}
|
||||
|
||||
getInitialState: -> @stateFromStore()
|
||||
|
||||
componentDidMount: ->
|
||||
@$el = $(@getDOMNode())
|
||||
@$input = @$el.find('input')
|
||||
|
||||
StationStore.addChangeListener @_onChangeStore
|
||||
if @state.listening.indexOf(@state.station) is -1
|
||||
StationActions.listenStation @state.station
|
||||
|
||||
componentWillUnmount: ->
|
||||
StationStore.removeChangeListener @_onChangeStore
|
||||
|
||||
_toggleAudi: (e) ->
|
||||
$e = $(e.target).closest('.station')
|
||||
station = $e.find('.path').text()
|
||||
StationActions.toggleAudience station
|
||||
|
||||
_onChangeStore: ->
|
||||
@setState @stateFromStore()
|
||||
|
||||
_keyUp: (e) ->
|
||||
if e.keyCode is 13
|
||||
v = @$input.val()
|
||||
if @state.configs[@state.station].sources.indexOf(v) is -1
|
||||
_sources = _.clone @state.configs[@state.station].sources
|
||||
_sources.push v
|
||||
StationActions.setSources @state.station,_sources
|
||||
@$input.val('')
|
||||
|
||||
_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
|
||||
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}
|
||||
else
|
||||
members = ""
|
||||
|
||||
sourceInput = [(input {className:"join",onKeyUp:@_keyUp,placeholder:"+"}, "")]
|
||||
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},"×")
|
||||
])
|
||||
|
||||
else
|
||||
sources = ""
|
||||
|
||||
station = []
|
||||
station.push (a {className:"up",href:"\#/"}, [(div {className:"arow-up"}, "")])
|
||||
station.push (h1 {},@state.station)
|
||||
station.push (div {id:"members"},members)
|
||||
|
||||
parts.push (div {id:"station-container"}, (div {id:"station-meta"},station))
|
||||
parts.push (div {id:"sources-container"}, [(div {class:"sources-list"},sources),sourceCtrl])
|
||||
|
||||
div {id:"station"},parts
|
61
main/pub/talk/src/js/components/StationsComponent.coffee
Normal file
61
main/pub/talk/src/js/components/StationsComponent.coffee
Normal file
@ -0,0 +1,61 @@
|
||||
recl = React.createClass
|
||||
[div,input] = [React.DOM.div,React.DOM.input]
|
||||
|
||||
StationStore = require '../stores/StationStore.coffee'
|
||||
StationActions = require '../actions/StationActions.coffee'
|
||||
|
||||
module.exports = recl
|
||||
stateFromStore: -> {
|
||||
stations: StationStore.getStations()
|
||||
station: StationStore.getStation()
|
||||
}
|
||||
|
||||
getInitialState: -> @stateFromStore()
|
||||
|
||||
componentDidMount: ->
|
||||
@$el = $(@getDOMNode())
|
||||
@$add = $('#stations .add')
|
||||
@$input = @$el.find('input')
|
||||
StationStore.addChangeListener @_onChangeStore
|
||||
|
||||
componentWillUnmount: ->
|
||||
StationStore.removeChangeListener @_onChangeStore
|
||||
|
||||
_onChangeStore: -> @setState @stateFromStore()
|
||||
|
||||
_click: (e) ->
|
||||
s = $(e.target).closest('.station').find('.name').text()
|
||||
window.location.hash = "/#{s.toLowerCase()}"
|
||||
|
||||
_keyUp: (e) ->
|
||||
if e.keyCode is 13
|
||||
v = @$input.val().toLowerCase()
|
||||
if @state.stations.indexOf(v) is -1
|
||||
StationActions.createStation v
|
||||
@$input.val('')
|
||||
@$input.blur()
|
||||
|
||||
_remove: (e) ->
|
||||
_station = $(e.target).parent().find('.name').text()
|
||||
_stations = _.without @state.stations,_station
|
||||
StationActions.removeStation _station,_stations
|
||||
e.stopPropagation()
|
||||
e.preventDefault()
|
||||
|
||||
render: ->
|
||||
station = @state.station
|
||||
_click = @_click
|
||||
_remove = @_remove
|
||||
stations = @state.stations.map (_station) ->
|
||||
k = "station"
|
||||
parts = [(div {className:"name"}, _station.name)]
|
||||
if _station.name isnt window.util.mainStation()
|
||||
parts.push (div {className:"remove",onClick:_remove,dataStation:_station.name},"×")
|
||||
div {className:k,onClick:_click},parts
|
||||
|
||||
div {id:"stations"}, [
|
||||
div {className:"stations"},stations
|
||||
div {className:"join-ctrl"}, [
|
||||
input {className:"join",onKeyUp:@_keyUp,placeholder:"+"}, ""
|
||||
]
|
||||
]
|
130
main/pub/talk/src/js/components/WritingComponent.coffee
Normal file
130
main/pub/talk/src/js/components/WritingComponent.coffee
Normal file
@ -0,0 +1,130 @@
|
||||
recl = React.createClass
|
||||
[div,input,textarea] = [React.DOM.div,React.DOM.input,React.DOM.textarea]
|
||||
|
||||
MessageActions = require '../actions/MessageActions.coffee'
|
||||
StationActions = require '../actions/StationActions.coffee'
|
||||
StationStore = require '../stores/StationStore.coffee'
|
||||
Member = require './MemberComponent.coffee'
|
||||
|
||||
module.exports = recl
|
||||
set: ->
|
||||
if window.localStorage and @$writing then window.localStorage.setItem 'writing', @$writing.text()
|
||||
|
||||
get: ->
|
||||
if window.localStorage then window.localStorage.getItem 'writing'
|
||||
|
||||
stateFromStore: -> {
|
||||
audi:StationStore.getAudience()
|
||||
members:StationStore.getMembers()
|
||||
typing:StationStore.getTyping()
|
||||
station:StationStore.getStation()
|
||||
}
|
||||
|
||||
getInitialState: -> @stateFromStore()
|
||||
|
||||
typing: (state) ->
|
||||
if @state.typing[@state.station] isnt state
|
||||
StationActions.setTyping @state.station,state
|
||||
|
||||
_blur: ->
|
||||
MessageActions.setTyping false
|
||||
@typing false
|
||||
|
||||
_focus: ->
|
||||
MessageActions.setTyping true
|
||||
@typing true
|
||||
|
||||
sendMessage: ->
|
||||
MessageActions.sendMessage @state.station,@$writing.text(),@state.audi
|
||||
@$length.text "0/69"
|
||||
@$writing.text('')
|
||||
@set()
|
||||
@typing false
|
||||
|
||||
_keyDown: (e) ->
|
||||
if e.keyCode is 13
|
||||
e.preventDefault()
|
||||
@sendMessage()
|
||||
return false
|
||||
@_input()
|
||||
@set()
|
||||
|
||||
_input: (e) ->
|
||||
text = @$writing.text()
|
||||
length = text.length
|
||||
geturl = new RegExp "(^|[ \t\r\n])((ftp|http|https|gopher|mailto|news|nntp|telnet|wais|file|prospero|aim|webcal):(([A-Za-z0-9$_.+!*(),;/?:@&~=-])|%[A-Fa-f0-9]{2}){2,}(#([a-zA-Z0-9][a-zA-Z0-9$_.+!*(),;/?:@&~=%-]*))?([A-Za-z0-9$_+!*();/?:~-]))", "g"
|
||||
urls = text.match(geturl)
|
||||
if urls isnt null and urls.length > 0
|
||||
for url in urls
|
||||
length -= url.length
|
||||
length += 10
|
||||
@$length.text "#{length}/69"
|
||||
if length >= 69
|
||||
@$writing.text(@$writing.text().substr(0,69))
|
||||
@cursorAtEnd()
|
||||
e.preventDefault() if e
|
||||
return false
|
||||
|
||||
_setFocus: -> @$writing.focus()
|
||||
|
||||
getTime: ->
|
||||
d = new Date()
|
||||
seconds = d.getSeconds()
|
||||
if seconds < 10
|
||||
seconds = "0" + seconds
|
||||
"~"+d.getHours() + "." + d.getMinutes() + "." + seconds
|
||||
|
||||
cursorAtEnd: ->
|
||||
range = document.createRange()
|
||||
range.selectNodeContents @$writing[0]
|
||||
range.collapse(false)
|
||||
selection = window.getSelection()
|
||||
selection.removeAllRanges()
|
||||
selection.addRange(range)
|
||||
|
||||
componentDidMount: ->
|
||||
window.util.sendMessage = @sendMessage
|
||||
StationStore.addChangeListener @_onChangeStore
|
||||
@$el = $ @getDOMNode()
|
||||
@$length = $('#length')
|
||||
@$writing = $('#writing')
|
||||
@$writing.focus()
|
||||
if @get()
|
||||
@$writing.text @get()
|
||||
@_input()
|
||||
@interval = setInterval =>
|
||||
@$el.find('.time').text @getTime()
|
||||
, 1000
|
||||
|
||||
componentWillUnmount: ->
|
||||
StationStore.removeChangeListener @_onChangeStore
|
||||
clearInterval @interval
|
||||
|
||||
_onChangeStore: -> @setState @stateFromStore()
|
||||
|
||||
render: ->
|
||||
user = "~"+window.urb.user
|
||||
iden = StationStore.getMember(user)
|
||||
ship = if iden then iden.ship else user
|
||||
name = if iden then iden.name else ""
|
||||
|
||||
k = "writing"
|
||||
k+= " hidden" if not @state?.station
|
||||
|
||||
div {className:k,onClick:@_setFocus}, [
|
||||
(div {className:"attr"}, [
|
||||
(Member iden, "")
|
||||
(div {className:"time"}, @getTime())
|
||||
])
|
||||
(div {
|
||||
id:"writing"
|
||||
contentEditable:true
|
||||
onFocus: @_focus
|
||||
onBlur: @_blur
|
||||
onInput: @_input
|
||||
onPaste: @_input
|
||||
onKeyDown: @_keyDown
|
||||
onFocus: @cursorAtEnd
|
||||
}, "")
|
||||
div {id:"length"}, "0/69"
|
||||
]
|
13
main/pub/talk/src/js/dispatcher/Dispatcher.coffee
Normal file
13
main/pub/talk/src/js/dispatcher/Dispatcher.coffee
Normal file
@ -0,0 +1,13 @@
|
||||
Dispatcher = require('flux').Dispatcher
|
||||
|
||||
module.exports = _.merge new Dispatcher(), {
|
||||
handleServerAction: (action) ->
|
||||
@dispatch
|
||||
source: 'server'
|
||||
action: action
|
||||
|
||||
handleViewAction: (action) ->
|
||||
@dispatch
|
||||
source: 'view'
|
||||
action: action
|
||||
}
|
111
main/pub/talk/src/js/main.coffee
Normal file
111
main/pub/talk/src/js/main.coffee
Normal file
@ -0,0 +1,111 @@
|
||||
$(() ->
|
||||
StationActions = require './actions/StationActions.coffee'
|
||||
|
||||
rend = React.render
|
||||
|
||||
window.chat = {}
|
||||
window.chat.MessagePersistence = require './persistence/MessagePersistence.coffee'
|
||||
window.chat.StationPersistence = require './persistence/StationPersistence.coffee'
|
||||
|
||||
window.util =
|
||||
mainStation: ->
|
||||
switch window.urb.user.length
|
||||
when 3
|
||||
return "court"
|
||||
when 5
|
||||
return "floor"
|
||||
when 13
|
||||
return "porch"
|
||||
|
||||
create: (name) ->
|
||||
window.chat.StationPersistence.createStation name, (err,res) ->
|
||||
|
||||
subscribe: (name) ->
|
||||
window.chat.StationPersistence.addSource "main",window.urb.ship,["~zod/#{name}"]
|
||||
|
||||
uuid32: ->
|
||||
str = "0v"
|
||||
str += Math.ceil(Math.random()*8)+"."
|
||||
for i in [0..5]
|
||||
_str = Math.ceil(Math.random()*10000000).toString(32)
|
||||
_str = ("00000"+_str).substr(-5,5)
|
||||
str += _str+"."
|
||||
str.slice(0,-1)
|
||||
|
||||
populate: (station,number) ->
|
||||
c = 0
|
||||
send = ->
|
||||
if c < number
|
||||
c++
|
||||
else
|
||||
console.log 'done'
|
||||
return true
|
||||
_audi = {}
|
||||
_audi[station] = "pending"
|
||||
_message =
|
||||
serial:window.util.uuid32()
|
||||
audience:_audi
|
||||
statement:
|
||||
speech:
|
||||
say:"Message "+c
|
||||
time: Date.now()
|
||||
now: Date.now()
|
||||
window.chat.MessagePersistence.sendMessage _message,send
|
||||
send()
|
||||
|
||||
getScroll: ->
|
||||
@writingPosition = $('#messaging-container').outerHeight(true)+$('#messaging-container').offset().top-$(window).height()
|
||||
#@writingPosition = $('#writing-container').position().top-$(window).height()+$('#writing-container').outerHeight(true)
|
||||
|
||||
setScroll: ->
|
||||
window.util.getScroll()
|
||||
$(window).scrollTop(window.util.writingPosition)
|
||||
|
||||
checkScroll: ->
|
||||
if not window.util.writingPosition
|
||||
window.util.getScroll()
|
||||
if $(window).scrollTop() < window.util.writingPosition
|
||||
$('body').addClass 'scrolling'
|
||||
else
|
||||
$('body').removeClass 'scrolling'
|
||||
|
||||
$(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 $('#messages-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()
|
||||
)
|
File diff suppressed because it is too large
Load Diff
12
main/pub/talk/src/js/package.json
Normal file
12
main/pub/talk/src/js/package.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"name": "urbit-radio",
|
||||
"version": "0.0.0",
|
||||
"description": "urbit radio frontend",
|
||||
"main": "main.js",
|
||||
"dependencies": {
|
||||
"coffeeify": "~0.7.0",
|
||||
"flux": "~2.0.1",
|
||||
"lodash": "~2.4.1",
|
||||
"moment-timezone": "~0.2.4"
|
||||
}
|
||||
}
|
43
main/pub/talk/src/js/persistence/MessagePersistence.coffee
Normal file
43
main/pub/talk/src/js/persistence/MessagePersistence.coffee
Normal file
@ -0,0 +1,43 @@
|
||||
MessageActions = require '../actions/MessageActions.coffee'
|
||||
|
||||
module.exports =
|
||||
listenStation: (station,since) ->
|
||||
window.urb.subscribe {
|
||||
appl:"rodeo"
|
||||
path:"/f/#{station}/#{since}"
|
||||
}, (err,res) ->
|
||||
console.log('m subscription updates')
|
||||
console.log(res.data)
|
||||
if res.data.ok is true
|
||||
MessageActions.listeningStation station
|
||||
if res.data?.grams?.tele
|
||||
MessageActions.loadMessages res.data.grams
|
||||
|
||||
get: (station,start,end) ->
|
||||
window.urb.subscribe {
|
||||
appl:"rodeo"
|
||||
path:"/f/#{station}/#{end}/#{start}"
|
||||
}, (err,res) ->
|
||||
console.log 'get'
|
||||
console.log res
|
||||
if res.data?.grams?.tele
|
||||
MessageActions.loadMessages res.data.grams,true
|
||||
window.urb.unsubscribe {
|
||||
appl:"rodeo"
|
||||
path:"/f/#{station}/#{end}/#{start}"
|
||||
}, (err,res) ->
|
||||
console.log 'done'
|
||||
console.log res
|
||||
|
||||
sendMessage: (message,cb) ->
|
||||
window.urb.send {
|
||||
appl:"rodeo"
|
||||
mark:"rodeo-command"
|
||||
data:
|
||||
publish: [
|
||||
message
|
||||
]
|
||||
}, (err,res) ->
|
||||
console.log 'sent'
|
||||
console.log arguments
|
||||
cb(err,res) if cb
|
74
main/pub/talk/src/js/persistence/StationPersistence.coffee
Normal file
74
main/pub/talk/src/js/persistence/StationPersistence.coffee
Normal file
@ -0,0 +1,74 @@
|
||||
StationActions = require '../actions/StationActions.coffee'
|
||||
|
||||
module.exports =
|
||||
createStation: (name,cb) ->
|
||||
window.urb.send {
|
||||
appl:"rodeo"
|
||||
mark:"rodeo-command"
|
||||
data:
|
||||
design:
|
||||
party:name
|
||||
config:
|
||||
sources:[]
|
||||
caption:""
|
||||
cordon:{posture:"white", list:[]}
|
||||
}, cb
|
||||
|
||||
removeStation: (name,cb) ->
|
||||
window.urb.send {
|
||||
appl:"rodeo"
|
||||
mark:"rodeo-command"
|
||||
data:
|
||||
design:
|
||||
party:name
|
||||
config:null
|
||||
}, cb
|
||||
|
||||
setSources: (station,ship,sources) ->
|
||||
send =
|
||||
appl:"rodeo"
|
||||
mark:"rodeo-command"
|
||||
data:
|
||||
design:
|
||||
party:station
|
||||
config:
|
||||
sources:sources
|
||||
caption:""
|
||||
cordon:{posture:"white", list:[]}
|
||||
window.urb.send send, (err,res) ->
|
||||
console.log 'add source updates'
|
||||
console.log arguments
|
||||
|
||||
members: ->
|
||||
window.urb.subscribe {
|
||||
appl:"rodeo"
|
||||
path:"/a/court"
|
||||
}, (err,res) ->
|
||||
console.log 'membership updates'
|
||||
console.log res.data
|
||||
if res.data?.group?.global
|
||||
StationActions.loadMembers res.data.group.global
|
||||
|
||||
listen: ->
|
||||
window.urb.subscribe {
|
||||
appl:"rodeo"
|
||||
path:"/"
|
||||
}, (err,res) ->
|
||||
console.log 'house updates'
|
||||
console.log res.data
|
||||
if res.data.house
|
||||
StationActions.loadStations res.data.house
|
||||
|
||||
listenStation: (station) ->
|
||||
window.urb.subscribe {
|
||||
appl:"rodeo"
|
||||
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.config
|
||||
StationActions.loadConfig station,res.data.config
|
101
main/pub/talk/src/js/stores/MessageStore.coffee
Normal file
101
main/pub/talk/src/js/stores/MessageStore.coffee
Normal file
@ -0,0 +1,101 @@
|
||||
moment = require 'moment-timezone'
|
||||
|
||||
EventEmitter = require('events').EventEmitter
|
||||
|
||||
MessageDispatcher = require '../dispatcher/Dispatcher.coffee'
|
||||
|
||||
_messages = {}
|
||||
_fetching = false
|
||||
_last = null
|
||||
_station = null
|
||||
_listening = []
|
||||
_typing = false
|
||||
|
||||
MessageStore = _.merge new EventEmitter,{
|
||||
removeChangeListener: (cb) -> @removeListener "change", cb
|
||||
|
||||
emitChange: -> @emit 'change'
|
||||
|
||||
addChangeListener: (cb) -> @on 'change', cb
|
||||
|
||||
leadingZero: (str) ->
|
||||
if Number(str) < 10 then "0"+str else str
|
||||
|
||||
convertDate: (time) ->
|
||||
time = time.substr(1).split(".")
|
||||
time[1] = @leadingZero time[1]
|
||||
time[2] = @leadingZero time[2]
|
||||
d = new moment "#{time[0]}-#{time[1]}-#{time[2]}T#{time[4]}:#{time[5]}:#{time[6]}Z"
|
||||
d.tz "Europe/London"
|
||||
d
|
||||
|
||||
getListening: -> _listening
|
||||
|
||||
getTyping: -> _typing
|
||||
|
||||
setTyping: (state) -> _typing = state
|
||||
|
||||
setListening: (station) ->
|
||||
if _listening.indexOf(station) isnt -1
|
||||
console.log 'already listening on that station (somehow).'
|
||||
else
|
||||
_listening.push station
|
||||
|
||||
setStation: (station) -> _station = station
|
||||
|
||||
sendMessage: (message) ->
|
||||
_messages[message.thought.serial] = message
|
||||
|
||||
loadMessages: (messages,last,get) ->
|
||||
for k,v of messages
|
||||
serial = v.thought.serial
|
||||
v.key = serial
|
||||
# always overwrite with new
|
||||
_messages[serial] = v
|
||||
_last = last if last < _last or _last is null or get is true
|
||||
_fetching = false
|
||||
|
||||
getAll: -> _.values _messages
|
||||
|
||||
getFetching: -> _fetching
|
||||
|
||||
setFetching: (state) -> _fetching = state
|
||||
|
||||
getLast: -> _last
|
||||
}
|
||||
|
||||
MessageStore.setMaxListeners 100
|
||||
|
||||
MessageStore.dispatchToken = MessageDispatcher.register (payload) ->
|
||||
action = payload.action
|
||||
|
||||
switch action.type
|
||||
when 'station-switch'
|
||||
MessageStore.setStation action.station
|
||||
break
|
||||
when 'messages-listen'
|
||||
MessageStore.setListening action.station
|
||||
MessageStore.emitChange()
|
||||
break
|
||||
when 'messages-typing'
|
||||
MessageStore.setTyping action.state
|
||||
MessageStore.emitChange()
|
||||
break
|
||||
when 'messages-fetch'
|
||||
MessageStore.setFetching true
|
||||
MessageStore.emitChange()
|
||||
break
|
||||
when 'messages-load'
|
||||
MessageStore.loadMessages action.messages,action.last,action.get
|
||||
MessageStore.emitChange()
|
||||
break
|
||||
when 'message-load'
|
||||
MessageStore.loadMessage action.time,action.message,action.author
|
||||
MessageStore.emitChange()
|
||||
break
|
||||
when 'message-send'
|
||||
MessageStore.sendMessage action.message
|
||||
MessageStore.emitChange()
|
||||
break
|
||||
|
||||
module.exports = MessageStore
|
132
main/pub/talk/src/js/stores/StationStore.coffee
Normal file
132
main/pub/talk/src/js/stores/StationStore.coffee
Normal file
@ -0,0 +1,132 @@
|
||||
EventEmitter = require('events').EventEmitter
|
||||
|
||||
StationDispatcher = require '../dispatcher/Dispatcher.coffee'
|
||||
|
||||
_audience = []
|
||||
_members = {}
|
||||
_stations = []
|
||||
_listening = []
|
||||
_station = null
|
||||
_config = {}
|
||||
_typing = {}
|
||||
|
||||
StationStore = _.merge new EventEmitter,{
|
||||
removeChangeListener: (cb) -> @removeListener "change", cb
|
||||
|
||||
emitChange: -> @emit 'change'
|
||||
|
||||
addChangeListener: (cb) -> @on 'change', cb
|
||||
|
||||
getAudience: -> _audience
|
||||
|
||||
setAudience: (audience) -> _audience = audience
|
||||
|
||||
toggleAudience: (station) ->
|
||||
if _audience.indexOf(station) isnt -1
|
||||
_audience.splice _audience.indexOf(station), 1
|
||||
else
|
||||
_audience.push station
|
||||
|
||||
loadConfig: (station,config) -> _config[station] = config
|
||||
|
||||
getConfigs: -> _config
|
||||
|
||||
getConfig: (station) -> _config[station]
|
||||
|
||||
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
|
||||
|
||||
getMembers: -> _members
|
||||
|
||||
getListening: -> _listening
|
||||
|
||||
setListening: (station) ->
|
||||
if _listening.indexOf(station) isnt -1
|
||||
console.log 'already listening on that station (somehow).'
|
||||
else
|
||||
_listening.push station
|
||||
|
||||
createStation: (station) ->
|
||||
_stations.push(station) if _stations.indexOf(station) is -1
|
||||
|
||||
loadStations: (stations) -> _stations = stations
|
||||
|
||||
getStations: -> _stations
|
||||
|
||||
setStation: (station) -> _station = station
|
||||
|
||||
unsetStation: (station) ->
|
||||
_station = null if _station is station
|
||||
|
||||
getStation: -> _station
|
||||
|
||||
joinStation: (station) ->
|
||||
if _config.court?.sources.indexOf(station) is -1
|
||||
_config.court.sources.push station
|
||||
|
||||
getTyping: () -> _typing
|
||||
|
||||
setTyping: (station,state) ->
|
||||
for k,v of _typing
|
||||
_typing[k] = (k is station)
|
||||
_typing[station] = state
|
||||
}
|
||||
|
||||
StationStore.setMaxListeners 100
|
||||
|
||||
StationStore.dispatchToken = StationDispatcher.register (payload) ->
|
||||
action = payload.action
|
||||
|
||||
switch action.type
|
||||
when 'station-audience-toggle'
|
||||
StationStore.toggleAudience action.station
|
||||
StationStore.emitChange()
|
||||
break
|
||||
when 'station-set-audience'
|
||||
StationStore.setAudience action.audience
|
||||
StationStore.emitChange()
|
||||
break
|
||||
when 'station-switch'
|
||||
StationStore.setAudience []
|
||||
StationStore.setStation action.station
|
||||
StationStore.emitChange()
|
||||
break
|
||||
when 'station-listen'
|
||||
StationStore.setListening action.station
|
||||
StationStore.emitChange()
|
||||
break
|
||||
when "config-load"
|
||||
StationStore.loadConfig action.station,action.config
|
||||
StationStore.emitChange()
|
||||
break
|
||||
when "stations-load"
|
||||
StationStore.loadStations action.stations
|
||||
StationStore.emitChange()
|
||||
break
|
||||
when "stations-leave"
|
||||
StationStore.loadStations action.stations
|
||||
StationStore.unsetStation action.station
|
||||
StationStore.emitChange()
|
||||
break
|
||||
when "station-create"
|
||||
StationStore.createStation action.station
|
||||
StationStore.emitChange()
|
||||
break
|
||||
when "members-load"
|
||||
StationStore.loadMembers action.station,action.members
|
||||
StationStore.emitChange()
|
||||
break
|
||||
when "typing-set"
|
||||
StationStore.setTyping action.station,action.state
|
||||
StationStore.emitChange()
|
||||
break
|
||||
|
||||
module.exports = StationStore
|
19439
main/pub/talk/src/js/test.js
Normal file
19439
main/pub/talk/src/js/test.js
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user