From c43e3116743bd9e8b5d46b4ed52653922d996ed1 Mon Sep 17 00:00:00 2001 From: Anton Dyudin Date: Fri, 23 Oct 2015 16:06:28 -0700 Subject: [PATCH 1/4] WritingComponent cleaning --- .../src/js/components/WritingComponent.coffee | 140 +++++++++++------- 1 file changed, 88 insertions(+), 52 deletions(-) diff --git a/pub/talk/src/js/components/WritingComponent.coffee b/pub/talk/src/js/components/WritingComponent.coffee index 0b065f0285..a459f2495f 100644 --- a/pub/talk/src/js/components/WritingComponent.coffee +++ b/pub/talk/src/js/components/WritingComponent.coffee @@ -7,6 +7,68 @@ StationActions = require '../actions/StationActions.coffee' StationStore = require '../stores/StationStore.coffee' Member = require './MemberComponent.coffee' +SHIPSHAPE = /// +^~?( #preamble + [a-z]{3} # galaxy + | [a-z]{6}(-[a-z]{6}){0,3} # star - moon + | [a-z]{6}(-[a-z]{6}){3} # comet + (--[a-z]{6}(-[a-z]{6}){3})+ # +)$ #postamble +/// +PO = ''' +dozmarbinwansamlitsighidfidlissogdirwacsabwissib +rigsoldopmodfoglidhopdardorlorhodfolrintogsilmir +holpaslacrovlivdalsatlibtabhanticpidtorbolfosdot +losdilforpilramtirwintadbicdifrocwidbisdasmidlop +rilnardapmolsanlocnovsitnidtipsicropwitnatpanmin +ritpodmottamtolsavposnapnopsomfinfonbanporworsip +ronnorbotwicsocwatdolmagpicdavbidbaltimtasmallig +sivtagpadsaldivdactansidfabtarmonranniswolmispal +lasdismaprabtobrollatlonnodnavfignomnibpagsopral +bilhaddocridmocpacravripfaltodtiltinhapmicfanpat +taclabmogsimsonpinlomrictapfirhasbosbatpochactid +havsaplindibhosdabbitbarracparloddosbortochilmac +tomdigfilfasmithobharmighinradmashalraglagfadtop +mophabnilnosmilfopfamdatnoldinhatnacrisfotribhoc +nimlarfitwalrapsarnalmoslandondanladdovrivbacpol +laptalpitnambonrostonfodponsovnocsorlavmatmipfap + +zodnecbudwessevpersutletfulpensytdurwepserwylsun +rypsyxdyrnuphebpeglupdepdysputlughecryttyvsydnex +lunmeplutseppesdelsulpedtemledtulmetwenbynhexfeb +pyldulhetmevruttylwydtepbesdexsefwycburderneppur +rysrebdennutsubpetrulsynregtydsupsemwynrecmegnet +secmulnymtevwebsummutnyxrextebfushepbenmuswyxsym +selrucdecwexsyrwetdylmynmesdetbetbeltuxtugmyrpel +syptermebsetdutdegtexsurfeltudnuxruxrenwytnubmed +lytdusnebrumtynseglyxpunresredfunrevrefmectedrus +bexlebduxrynnumpyxrygryxfeptyrtustyclegnemfermer +tenlusnussyltecmexpubrymtucfyllepdebbermughuttun +bylsudpemdevlurdefbusbeprunmelpexdytbyttyplevmyl +wedducfurfexnulluclennerlexrupnedlecrydlydfenwel +nydhusrelrudneshesfetdesretdunlernyrsebhulryllud +remlysfynwerrycsugnysnyllyndyndemluxfedsedbecmun +lyrtesmudnytbyrsenwegfyrmurtelreptegpecnelnevfes +''' + +Audience = recl + displayName: "Audience" + onKeyDown: (e) -> + if e.keyCode is 13 + e.preventDefault() + setTimeout () -> + $('#writing').focus() + ,0 + return false + render: -> + div { + id:"audi" + className:"audi valid-#{@props.valid}" + contentEditable:true + @onKeyDown + onBlur:@props.onBlur + }, @props.audi.join(" ") + module.exports = recl set: -> if window.localStorage and @$writing then window.localStorage.setItem 'writing', @$writing.text() @@ -26,20 +88,21 @@ module.exports = recl s.ludi = _.without s.ludi, window.util.mainStationPath window.urb.user s - getInitialState: -> @stateFromStore() + getInitialState: -> _.extend @stateFromStore(), length:0, lengthy: false typing: (state) -> if @state.typing[@state.station] isnt state StationActions.setTyping @state.station,state - _blur: -> + onBlur: -> @$writing.text @$writing.text() MessageActions.setTyping false @typing false - _focus: -> + onFocus: -> MessageActions.setTyping true @typing true + @cursorAtEnd addCC: (audi) -> listening = @state.config[window.util.mainStation(window.urb.user)].sources @@ -62,22 +125,14 @@ module.exports = recl audi = @state.audi audi = @addCC audi MessageActions.sendMessage @$writing.text().trim(),audi - @$length.text "0/62" @$writing.text('') + @setState length:0 @set() @typing false - _audiKeyDown: (e) -> - if e.keyCode is 13 - e.preventDefault() - setTimeout () -> - $('#writing').focus() - ,0 - return false - - _writingKeyUp: (e) -> + onKeyUp: (e) -> if not window.urb.util.isURL @$writing.text() - @$length.toggleClass('valid-false',(@$writing.text().length > 62)) + @setState lengthy: (@$writing.text().length > 62) # r = window.getSelection().getRangeAt(0).cloneRange() # @$writing.text @$writing.text() # setTimeout => @@ -87,7 +142,7 @@ module.exports = recl # console.log r # ,0 - _writingKeyDown: (e) -> + onKeyDown: (e) -> if e.keyCode is 13 txt = @$writing.text() e.preventDefault() @@ -95,10 +150,10 @@ module.exports = recl window.urb.util.isURL @$writing.text() ) @sendMessage() return false - @_input() + @onInput() @set() - _input: (e) -> + onInput: (e) -> text = @$writing.text() length = text.length # geturl = new RegExp [ @@ -113,11 +168,10 @@ module.exports = recl # for url in urls # length -= url.length # length += 10 - @$length.text "#{length}/62" - - _setFocus: -> @$writing.focus() + @setState {length} _validateAudiPart: (a) -> + a = a.trim() # if a[0] isnt "~" # return false if a.indexOf("/") isnt -1 @@ -127,9 +181,9 @@ module.exports = recl ship = _a[0] else ship = a - if ship.length < 3 - return false - return true + + return (SHIPSHAPE.test ship) and + _.all (ship.match /[a-z]{3}/g), (a)-> -1 isnt PO.indexOf a _validateAudi: -> v = $('#audi').text() @@ -138,23 +192,17 @@ module.exports = recl return true if v.length < 5 # zod/a is shortest return false - v = v.split " " - for a in v - a = a.trim() - valid = @_validateAudiPart(a) - valid + _.all (v.split /\ +/), @_validateAudiPart _setAudi: -> valid = @_validateAudi() StationActions.setValidAudience valid if valid is true - v = $('#audi').text() - if v.length is 0 then v = window.util.mainStationPath window.urb.user - v = v.split " " - for k,_v of v - if _v[0] isnt "~" then v[k] = "~#{_v}" - StationActions.setAudience v - v + stan = $('#audi').text() || window.util.mainStationPath window.urb.user + stan = (stan.split /\ +/).map (v)-> + if v[0] is "~" then v else "~"+v + StationActions.setAudience stan + stan else false @@ -178,12 +226,11 @@ module.exports = recl StationStore.addChangeListener @_onChangeStore MessageStore.addChangeListener @_onChangeStore @$el = $ @getDOMNode() - @$length = $('#length') @$writing = $('#writing') @$writing.focus() if @get() @$writing.text @get() - @_input() + @onInput() @interval = setInterval => @$el.find('.time').text @getTime() , 1000 @@ -210,25 +257,14 @@ module.exports = recl div {className:k}, [ (div {className:"attr"}, [ (React.createElement Member, iden) - (div { - id:"audi" - className:"audi valid-#{@state.valid}" - contentEditable:true - onKeyDown: @_audiKeyDown - onBlur:@_setAudi - }, audi.join(" ")) + (React.createElement Audience, {audi,valid:@state.valid, onBlur:@_setAudi}) (div {className:"time"}, @getTime()) ]) (div { id:"writing" contentEditable:true - onFocus: @_focus - onBlur: @_blur - onInput: @_input - onPaste: @_input - onKeyDown: @_writingKeyDown - onKeyUp: @_writingKeyUp - onFocus: @cursorAtEnd + onPaste: @onInput + @onInput, @onFocus, @onBlur, @onKeyDown, @onKeyUp }, "") - div {id:"length"}, "0/62" + (div {id:"length",className:("valid-false" if @state.lengthy)}, "#{@state.length}/62") ] From e7ab4f7c7d087144cc44590035b8268cd9baec3d Mon Sep 17 00:00:00 2001 From: Anton Dyudin Date: Tue, 27 Oct 2015 16:43:08 -0700 Subject: [PATCH 2/4] message splitting --- mar/talk/command.hoon | 30 ++- mar/talk/report.hoon | 1 + pub/talk/src/js/actions/MessageActions.coffee | 41 ++- .../js/components/MessagesComponent.coffee | 19 +- .../src/js/components/WritingComponent.coffee | 11 +- pub/talk/src/js/main.js | 242 ++++++++++-------- 6 files changed, 205 insertions(+), 139 deletions(-) diff --git a/mar/talk/command.hoon b/mar/talk/command.hoon index bb039a0202..816f265234 100644 --- a/mar/talk/command.hoon +++ b/mar/talk/command.hoon @@ -27,6 +27,13 @@ ^- (unit (list ,_[(wonk *fel) (need *wit)])) (zl (turn (~(tap by a)) (head-rush fel))) :: + ++ ke :: callbacks + |* [gar=* sef=_|.(fist)] + |= jon=json + ^- (unit ,_gar) + =- ~! gar ~! (need -) - + ((sef) jon) + :: ++ as :: array as set :: |*(a=fist (cu sa (ar a))) :: XX types |* a=fist @@ -69,20 +76,31 @@ %+ sear (soft passport) ;~((glue fas) sym urs:ab) :: XX [a-z0-9_]{1,15} :: + ++ speeech ?(speech [%eval p=@t] [%mor p=(list speeech)]) ++ eval - |= a=(trel ,@da bouquet ?(speech [%eval p=@t])) ^- statement - ?. ?=(%eval -.r.a) a - =+ pax=[&1:% &2:% (scot %da p.a) |3:%] - =- a(r [%fat tank/- %exp p.r.a]) - p:(mule |.([(sell (slap !>(..zuse) (rain pax p.r.a)))]~)) + |= a=(trel ,@da bouquet speeech) ^- statement + %= a r ^- speech + |- + ?: ?=(%mor -.r.a) + [%mor (turn p.r.a |=(b=speeech ^$(r.a b)))] + ?. ?=(%eval -.r.a) r.a + =- [%fat tank/- %exp p.r.a] + =+ pax=[&1:% &2:% (scot %da p.a) |3:%] + p:(mule |.([(sell (slap !>(..zuse) (rain pax p.r.a)))]~)) + == :: ++ stam ^- $+(json (unit statement)) %+ cu eval - =- (ot date/di bouquet/(as (ar so)) speech/(of -) ~) + (ot date/di bouquet/(as (ar so)) speech/spec ~) + :: + ++ spec + %+ ke *speeech |. + %- of :~ lin/(ot say/bo txt/so ~) url/(su aurf:urlp) eval/so + mor/(ar spec) :: exp/(cu |=(a=cord [a ~]) so) :: inv/(ot ship/(su fed:ag) party/(su urs:ab) ~) == diff --git a/mar/talk/report.hoon b/mar/talk/report.hoon index 6764ca9f2d..6136ee4e8c 100644 --- a/mar/talk/report.hoon +++ b/mar/talk/report.hoon @@ -95,6 +95,7 @@ %tax (joba txt/(jape (rend-work-duty p.a))) %app (jobe txt/[%s q.a] src/[%s p.a] ~) %fat (jobe tor/(tors p.a) taf/$(a q.a) ~) + %mor a/(turn p.a spec) :: %inv (jobe ship/(jope p.a) party/[%s q.a] ~) == :: diff --git a/pub/talk/src/js/actions/MessageActions.coffee b/pub/talk/src/js/actions/MessageActions.coffee index 75bde06ae6..48e9543ad3 100644 --- a/pub/talk/src/js/actions/MessageActions.coffee +++ b/pub/talk/src/js/actions/MessageActions.coffee @@ -56,17 +56,30 @@ module.exports = else if window.urb.util.isURL(message) speech = url: message - _message = - ship:window.urb.ship - thought: - serial:serial - audience:_audi - statement: - bouquet:[] - speech:speech - date: Date.now() - - MessageDispatcher.handleViewAction - type:"message-send" - message:_message - window.talk.MessagePersistence.sendMessage _message.thought + speeches = + if speech.lin?.txt.length < 64 + [speech] + else + {say,txt} = speech.lin + txt.match(/(.{0,64} |.{64}|.+$)/g).map (s)-> + lin: {say, txt: + if s.slice -1 isnt " " + s + else s.slice 0,-1 + } + + for speech in speeches + _message = + ship:window.urb.ship + thought: + serial:window.util.uuid32() + audience:_audi + statement: + bouquet:[] + speech:speech + date: Date.now() + + MessageDispatcher.handleViewAction + type:"message-send" + message:_message + window.talk.MessagePersistence.sendMessage _message.thought diff --git a/pub/talk/src/js/components/MessagesComponent.coffee b/pub/talk/src/js/components/MessagesComponent.coffee index 13ade80c85..9a3c0a3785 100644 --- a/pub/talk/src/js/components/MessagesComponent.coffee +++ b/pub/talk/src/js/components/MessagesComponent.coffee @@ -29,6 +29,15 @@ Message = recl return if user.toLowerCase() is 'system' @props._handlePm user + renderSpeech: (speech)-> switch + when (con = speech.lin) or (con = speech.app) or + (con = speech.exp) or (con = speech.tax) + con.txt + when (con = speech.url) + (a {href:con.txt,target:"_blank"}, con.txt) + when (con = speech.mor) then con.map @renderSpeech + else "Unknown speech type:" + (" %"+x for x of speech).join '' + render: -> # pendingClass = if @props.pending isnt "received" then "pending" else "" delivery = _.uniq _.pluck @props.thought.audience, "delivery" @@ -50,14 +59,8 @@ Message = recl type = ['private','public'] type = type[Number(aude.indexOf(window.util.mainStationPath(window.urb.user)) is -1)] - - mess = switch - when (con = speech.lin) or (con = speech.app) or - (con = speech.exp) or (con = speech.tax) - con.txt - when (con = speech.url) - (a {href:con.txt,target:"_blank"}, con.txt) - else "Unknown speech type:" + (" %"+x for x of speech).join '' + + mess = @renderSpeech speech klass += switch when speech.app? then " say" diff --git a/pub/talk/src/js/components/WritingComponent.coffee b/pub/talk/src/js/components/WritingComponent.coffee index a459f2495f..640ecee530 100644 --- a/pub/talk/src/js/components/WritingComponent.coffee +++ b/pub/talk/src/js/components/WritingComponent.coffee @@ -70,6 +70,7 @@ Audience = recl }, @props.audi.join(" ") module.exports = recl + displayName: "Writing" set: -> if window.localStorage and @$writing then window.localStorage.setItem 'writing', @$writing.text() @@ -124,7 +125,8 @@ module.exports = recl else audi = @state.audi audi = @addCC audi - MessageActions.sendMessage @$writing.text().trim(),audi + txt = @$writing.text().trim().replace(/\xa0/g,' ') + MessageActions.sendMessage txt,audi @$writing.text('') @setState length:0 @set() @@ -141,13 +143,12 @@ module.exports = recl # s.addRange r # console.log r # ,0 - + onKeyDown: (e) -> if e.keyCode is 13 txt = @$writing.text() e.preventDefault() - if ( (txt.length > 0 and txt.length < 63) or - window.urb.util.isURL @$writing.text() ) + if txt.length > 0 @sendMessage() return false @onInput() @@ -266,5 +267,5 @@ module.exports = recl onPaste: @onInput @onInput, @onFocus, @onBlur, @onKeyDown, @onKeyUp }, "") - (div {id:"length",className:("valid-false" if @state.lengthy)}, "#{@state.length}/62") + (div {id:"length"}, "#{@state.length}/64 (#{Math.ceil @state.length / 64})") ] diff --git a/pub/talk/src/js/main.js b/pub/talk/src/js/main.js index d37968571f..c2e0427593 100644 --- a/pub/talk/src/js/main.js +++ b/pub/talk/src/js/main.js @@ -37,7 +37,7 @@ module.exports = { return window.talk.MessagePersistence.get(station, start, end); }, sendMessage: function(message, audience) { - var _audi, _message, k, serial, speech, v; + var _audi, _message, i, k, len, ref, ref1, results, say, serial, speech, speeches, txt, v; serial = window.util.uuid32(); audience = _.uniq(audience); _audi = {}; @@ -69,23 +69,36 @@ module.exports = { url: message }; } - _message = { - ship: window.urb.ship, - thought: { - serial: serial, - audience: _audi, - statement: { - bouquet: [], - speech: speech, - date: Date.now() + speeches = ((ref = speech.lin) != null ? ref.txt.length : void 0) < 64 ? [speech] : ((ref1 = speech.lin, say = ref1.say, txt = ref1.txt, ref1), txt.match(/(.{0,64} |.{64}|.+$)/g).map(function(s) { + return { + lin: { + say: say, + txt: s.slice(-1 !== " ") ? s : s.slice(0, -1) } - } - }; - MessageDispatcher.handleViewAction({ - type: "message-send", - message: _message - }); - return window.talk.MessagePersistence.sendMessage(_message.thought); + }; + })); + results = []; + for (i = 0, len = speeches.length; i < len; i++) { + speech = speeches[i]; + _message = { + ship: window.urb.ship, + thought: { + serial: window.util.uuid32(), + audience: _audi, + statement: { + bouquet: [], + speech: speech, + date: Date.now() + } + } + }; + MessageDispatcher.handleViewAction({ + type: "message-send", + message: _message + }); + results.push(window.talk.MessagePersistence.sendMessage(_message.thought)); + } + return results; } }; @@ -261,8 +274,31 @@ Message = recl({ } return this.props._handlePm(user); }, + renderSpeech: function(speech) { + var con, x; + switch (false) { + case !((con = speech.lin) || (con = speech.app) || (con = speech.exp) || (con = speech.tax)): + return con.txt; + case !(con = speech.url): + return a({ + href: con.txt, + target: "_blank" + }, con.txt); + case !(con = speech.mor): + return con.map(this.renderSpeech); + default: + return "Unknown speech type:" + ((function() { + var results; + results = []; + for (x in speech) { + results.push(" %" + x); + } + return results; + })()).join(''); + } + }, render: function() { - var attachments, aude, audi, con, delivery, klass, mess, name, ref1, speech, type, x; + var attachments, aude, audi, delivery, klass, mess, name, ref1, speech, type; delivery = _.uniq(_.pluck(this.props.thought.audience, "delivery")); klass = delivery.indexOf("received") !== -1 ? " received" : " pending"; speech = this.props.thought.statement.speech; @@ -295,26 +331,7 @@ Message = recl({ }); type = ['private', 'public']; type = type[Number(aude.indexOf(window.util.mainStationPath(window.urb.user)) === -1)]; - mess = (function() { - switch (false) { - case !((con = speech.lin) || (con = speech.app) || (con = speech.exp) || (con = speech.tax)): - return con.txt; - case !(con = speech.url): - return a({ - href: con.txt, - target: "_blank" - }, con.txt); - default: - return "Unknown speech type:" + ((function() { - var results; - results = []; - for (x in speech) { - results.push(" %" + x); - } - return results; - })()).join(''); - } - })(); + mess = this.renderSpeech(speech); klass += (function() { switch (false) { case speech.app == null: @@ -696,7 +713,7 @@ module.exports = recl({ },{"../actions/StationActions.coffee":2,"../stores/StationStore.coffee":20,"./MemberComponent.coffee":3}],6:[function(require,module,exports){ -var Member, MessageActions, MessageStore, StationActions, StationStore, br, div, input, recl, ref, textarea; +var Audience, Member, MessageActions, MessageStore, PO, SHIPSHAPE, StationActions, StationStore, br, div, input, recl, ref, textarea; recl = React.createClass; @@ -712,7 +729,34 @@ StationStore = require('../stores/StationStore.coffee'); Member = require('./MemberComponent.coffee'); +SHIPSHAPE = /^~?([a-z]{3}|[a-z]{6}(-[a-z]{6}){0,3}|[a-z]{6}(-[a-z]{6}){3}(--[a-z]{6}(-[a-z]{6}){3})+)$/; + +PO = 'dozmarbinwansamlitsighidfidlissogdirwacsabwissib\nrigsoldopmodfoglidhopdardorlorhodfolrintogsilmir\nholpaslacrovlivdalsatlibtabhanticpidtorbolfosdot\nlosdilforpilramtirwintadbicdifrocwidbisdasmidlop\nrilnardapmolsanlocnovsitnidtipsicropwitnatpanmin\nritpodmottamtolsavposnapnopsomfinfonbanporworsip\nronnorbotwicsocwatdolmagpicdavbidbaltimtasmallig\nsivtagpadsaldivdactansidfabtarmonranniswolmispal\nlasdismaprabtobrollatlonnodnavfignomnibpagsopral\nbilhaddocridmocpacravripfaltodtiltinhapmicfanpat\ntaclabmogsimsonpinlomrictapfirhasbosbatpochactid\nhavsaplindibhosdabbitbarracparloddosbortochilmac\ntomdigfilfasmithobharmighinradmashalraglagfadtop\nmophabnilnosmilfopfamdatnoldinhatnacrisfotribhoc\nnimlarfitwalrapsarnalmoslandondanladdovrivbacpol\nlaptalpitnambonrostonfodponsovnocsorlavmatmipfap\n\nzodnecbudwessevpersutletfulpensytdurwepserwylsun\nrypsyxdyrnuphebpeglupdepdysputlughecryttyvsydnex\nlunmeplutseppesdelsulpedtemledtulmetwenbynhexfeb\npyldulhetmevruttylwydtepbesdexsefwycburderneppur\nrysrebdennutsubpetrulsynregtydsupsemwynrecmegnet\nsecmulnymtevwebsummutnyxrextebfushepbenmuswyxsym\nselrucdecwexsyrwetdylmynmesdetbetbeltuxtugmyrpel\nsyptermebsetdutdegtexsurfeltudnuxruxrenwytnubmed\nlytdusnebrumtynseglyxpunresredfunrevrefmectedrus\nbexlebduxrynnumpyxrygryxfeptyrtustyclegnemfermer\ntenlusnussyltecmexpubrymtucfyllepdebbermughuttun\nbylsudpemdevlurdefbusbeprunmelpexdytbyttyplevmyl\nwedducfurfexnulluclennerlexrupnedlecrydlydfenwel\nnydhusrelrudneshesfetdesretdunlernyrsebhulryllud\nremlysfynwerrycsugnysnyllyndyndemluxfedsedbecmun\nlyrtesmudnytbyrsenwegfyrmurtelreptegpecnelnevfes'; + +Audience = recl({ + displayName: "Audience", + onKeyDown: function(e) { + if (e.keyCode === 13) { + e.preventDefault(); + setTimeout(function() { + return $('#writing').focus(); + }, 0); + return false; + } + }, + render: function() { + return div({ + id: "audi", + className: "audi valid-" + this.props.valid, + contentEditable: true, + onKeyDown: this.onKeyDown, + onBlur: this.props.onBlur + }, this.props.audi.join(" ")); + } +}); + module.exports = recl({ + displayName: "Writing", set: function() { if (window.localStorage && this.$writing) { return window.localStorage.setItem('writing', this.$writing.text()); @@ -738,21 +782,25 @@ module.exports = recl({ return s; }, getInitialState: function() { - return this.stateFromStore(); + return _.extend(this.stateFromStore(), { + length: 0, + lengthy: false + }); }, typing: function(state) { if (this.state.typing[this.state.station] !== state) { return StationActions.setTyping(this.state.station, state); } }, - _blur: function() { + onBlur: function() { this.$writing.text(this.$writing.text()); MessageActions.setTyping(false); return this.typing(false); }, - _focus: function() { + onFocus: function() { MessageActions.setTyping(true); - return this.typing(true); + this.typing(true); + return this.cursorAtEnd; }, addCC: function(audi) { var cc, i, len, listening, s; @@ -773,7 +821,7 @@ module.exports = recl({ return audi; }, sendMessage: function() { - var audi; + var audi, txt; if (this._validateAudi() === false) { $('#audi').focus(); return; @@ -784,50 +832,46 @@ module.exports = recl({ audi = this.state.audi; } audi = this.addCC(audi); - MessageActions.sendMessage(this.$writing.text().trim(), audi); - this.$length.text("0/62"); + txt = this.$writing.text().trim().replace(/\xa0/g, ' '); + MessageActions.sendMessage(txt, audi); this.$writing.text(''); + this.setState({ + length: 0 + }); this.set(); return this.typing(false); }, - _audiKeyDown: function(e) { - if (e.keyCode === 13) { - e.preventDefault(); - setTimeout(function() { - return $('#writing').focus(); - }, 0); - return false; - } - }, - _writingKeyUp: function(e) { + onKeyUp: function(e) { if (!window.urb.util.isURL(this.$writing.text())) { - return this.$length.toggleClass('valid-false', this.$writing.text().length > 62); + return this.setState({ + lengthy: this.$writing.text().length > 62 + }); } }, - _writingKeyDown: function(e) { + onKeyDown: function(e) { var txt; if (e.keyCode === 13) { txt = this.$writing.text(); e.preventDefault(); - if ((txt.length > 0 && txt.length < 63) || window.urb.util.isURL(this.$writing.text())) { + if (txt.length > 0) { this.sendMessage(); } return false; } - this._input(); + this.onInput(); return this.set(); }, - _input: function(e) { + onInput: function(e) { var length, text; text = this.$writing.text(); length = text.length; - return this.$length.text(length + "/62"); - }, - _setFocus: function() { - return this.$writing.focus(); + return this.setState({ + length: length + }); }, _validateAudiPart: function(a) { var _a, ship; + a = a.trim(); if (a.indexOf("/") !== -1) { _a = a.split("/"); if (_a[1].length === 0) { @@ -837,13 +881,12 @@ module.exports = recl({ } else { ship = a; } - if (ship.length < 3) { - return false; - } - return true; + return (SHIPSHAPE.test(ship)) && _.all(ship.match(/[a-z]{3}/g), function(a) { + return -1 !== PO.indexOf(a); + }); }, _validateAudi: function() { - var a, i, len, v, valid; + var v; v = $('#audi').text(); v = v.trim(); if (v.length === 0) { @@ -852,32 +895,23 @@ module.exports = recl({ if (v.length < 5) { return false; } - v = v.split(" "); - for (i = 0, len = v.length; i < len; i++) { - a = v[i]; - a = a.trim(); - valid = this._validateAudiPart(a); - } - return valid; + return _.all(v.split(/\ +/), this._validateAudiPart); }, _setAudi: function() { - var _v, k, v, valid; + var stan, valid; valid = this._validateAudi(); StationActions.setValidAudience(valid); if (valid === true) { - v = $('#audi').text(); - if (v.length === 0) { - v = window.util.mainStationPath(window.urb.user); - } - v = v.split(" "); - for (k in v) { - _v = v[k]; - if (_v[0] !== "~") { - v[k] = "~" + _v; + stan = $('#audi').text() || window.util.mainStationPath(window.urb.user); + stan = (stan.split(/\ +/)).map(function(v) { + if (v[0] === "~") { + return v; + } else { + return "~" + v; } - } - StationActions.setAudience(v); - return v; + }); + StationActions.setAudience(stan); + return stan; } else { return false; } @@ -905,12 +939,11 @@ module.exports = recl({ StationStore.addChangeListener(this._onChangeStore); MessageStore.addChangeListener(this._onChangeStore); this.$el = $(this.getDOMNode()); - this.$length = $('#length'); this.$writing = $('#writing'); this.$writing.focus(); if (this.get()) { this.$writing.text(this.get()); - this._input(); + this.onInput(); } return this.interval = setInterval((function(_this) { return function() { @@ -944,28 +977,25 @@ module.exports = recl({ div({ className: "attr" }, [ - React.createElement(Member, iden), div({ - id: "audi", - className: "audi valid-" + this.state.valid, - contentEditable: true, - onKeyDown: this._audiKeyDown, + React.createElement(Member, iden), React.createElement(Audience, { + audi: audi, + valid: this.state.valid, onBlur: this._setAudi - }, audi.join(" ")), div({ + }), div({ className: "time" }, this.getTime()) ]), div({ id: "writing", contentEditable: true, - onFocus: this._focus, - onBlur: this._blur, - onInput: this._input, - onPaste: this._input, - onKeyDown: this._writingKeyDown, - onKeyUp: this._writingKeyUp, - onFocus: this.cursorAtEnd + onPaste: this.onInput, + onInput: this.onInput, + onFocus: this.onFocus, + onBlur: this.onBlur, + onKeyDown: this.onKeyDown, + onKeyUp: this.onKeyUp }, ""), div({ id: "length" - }, "0/62") + }, this.state.length + "/64 (" + (Math.ceil(this.state.length / 64)) + ")") ]); } }); From 21ba1a7e482bebd20bf425fce028f4d5563d8db4 Mon Sep 17 00:00:00 2001 From: Anton Dyudin Date: Tue, 27 Oct 2015 17:32:02 -0700 Subject: [PATCH 3/4] url hostname length check --- ape/talk.hoon | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ape/talk.hoon b/ape/talk.hoon index cfac07d637..c27f858f7c 100644 --- a/ape/talk.hoon +++ b/ape/talk.hoon @@ -1872,12 +1872,12 @@ |- ^- tang =< ?+(. . [@ *] [.]~) ^- ?(tank tang) :: wrap single tanks ?+ -.sep [>sep<]~ - %exp leaf/"# {(trip p.sep)}" + %exp palm/[~ "#" " " ~]^~[leaf/(trip p.sep)] %lin leaf/"{?:(p.sep "" "@ ")}{(trip q.sep)}" %non ~ %app rose/[": " ~ ~]^~[leaf/"[{(trip p.sep)}]" leaf/(trip q.sep)] %tax leaf/(rend-work-duty p.sep) - %url leaf/"/ {(earf p.sep)}" + %url palm/[~ "/" " " ~]^~[leaf/(earf p.sep)] %mor ?~(p.sep ~ (weld $(p.sep t.p.sep) $(sep i.p.sep))) %fat (welp (tr-rend-tors p.sep) $(sep q.sep)) == @@ -1924,6 +1924,8 @@ :+ '/' '_' =+ hok=r.p.p.p.sep ~! hok + =- (swag [(sub (max 64 (lent -)) 64) 64] -) + ^- tape =< ?~(-.hok (reel p.hok .) +:(scow %if p.hok)) |=([a=span b=tape] ?~(b (trip a) (welp b '.' (trip a)))) :: From 62ad0152d23004c5e89d79c025efa18731cef74b Mon Sep 17 00:00:00 2001 From: Anton Dyudin Date: Tue, 27 Oct 2015 17:56:27 -0700 Subject: [PATCH 4/4] Close #311 "web ui message ordering" --- ape/talk.hoon | 1 + pub/talk/src/js/components/MessagesComponent.coffee | 4 +++- pub/talk/src/js/main.js | 12 +++++++----- pub/talk/src/js/stores/MessageStore.coffee | 7 ++++--- 4 files changed, 15 insertions(+), 9 deletions(-) diff --git a/ape/talk.hoon b/ape/talk.hoon index c27f858f7c..4223b03a95 100644 --- a/ape/talk.hoon +++ b/ape/talk.hoon @@ -307,6 +307,7 @@ [['[' ']'] u.active.she] =+ cha=(~(get by nik) q.rew) ?^ cha ~[u.cha ' '] + :: ~& [rew nik nak] =+ por=~(te-prom te man.she q.rew) (weld `tape`[p.p.rew por] `tape`[q.p.rew ' ' ~]) :: diff --git a/pub/talk/src/js/components/MessagesComponent.coffee b/pub/talk/src/js/components/MessagesComponent.coffee index 9a3c0a3785..1203b81ba6 100644 --- a/pub/talk/src/js/components/MessagesComponent.coffee +++ b/pub/talk/src/js/components/MessagesComponent.coffee @@ -80,6 +80,7 @@ Message = recl ] module.exports = recl + displayName: "Messages" pageSize: 50 paddingTop: 100 @@ -125,7 +126,8 @@ module.exports = recl sortedMessages: (messages) -> _.sortBy messages, (_message) -> _message.pending = _message.thought.audience[station] - _message.thought.statement.date + _message.key + #_message.thought.statement.date componentDidMount: -> MessageStore.addChangeListener @_onChangeStore diff --git a/pub/talk/src/js/main.js b/pub/talk/src/js/main.js index c2e0427593..9aeb98b91b 100644 --- a/pub/talk/src/js/main.js +++ b/pub/talk/src/js/main.js @@ -370,6 +370,7 @@ Message = recl({ }); module.exports = recl({ + displayName: "Messages", pageSize: 50, paddingTop: 100, stateFromStore: function() { @@ -419,7 +420,7 @@ module.exports = recl({ sortedMessages: function(messages) { return _.sortBy(messages, function(_message) { _message.pending = _message.thought.audience[station]; - return _message.thought.statement.date; + return _message.key; }); }, componentDidMount: function() { @@ -5928,11 +5929,12 @@ MessageStore = _.merge(new EventEmitter, { return _messages[message.thought.serial] = message; }, loadMessages: function(messages, last, get) { - var k, serial, v; - for (k in messages) { - v = messages[k]; + var i, key, len, serial, v; + key = last; + for (i = 0, len = messages.length; i < len; i++) { + v = messages[i]; serial = v.thought.serial; - v.key = serial; + v.key = key++; _messages[serial] = v; } if (last < _last || _last === null || get === true) { diff --git a/pub/talk/src/js/stores/MessageStore.coffee b/pub/talk/src/js/stores/MessageStore.coffee index 1bd902a29b..0407914ab6 100644 --- a/pub/talk/src/js/stores/MessageStore.coffee +++ b/pub/talk/src/js/stores/MessageStore.coffee @@ -52,9 +52,10 @@ MessageStore = _.merge new EventEmitter,{ _messages[message.thought.serial] = message loadMessages: (messages,last,get) -> - for k,v of messages + key = last + for v in messages serial = v.thought.serial - v.key = serial + v.key = key++ # always overwrite with new _messages[serial] = v _last = last if last < _last or _last is null or get is true @@ -103,4 +104,4 @@ MessageStore.dispatchToken = MessageDispatcher.register (payload) -> MessageStore.emitChange() break -module.exports = MessageStore \ No newline at end of file +module.exports = MessageStore