Fork me on GitHub
#hoplon
<
2016-11-22
>
onetom01:11:56

and as an evolution to the 1st example - if you prefer brevity at the cost of a little complexity:

(let [opt (partial radio :marital :status)]
        (row :p (* 2 p) :g g :b 1 :bc transparent-grey
             (opt :single "Single")
             (opt :married "Married")
             (opt :divorced "Divorced")
             (opt :widowed "Widowed")))

jumblerg04:11:15

@onetom: so i'm thinking we should do away with elems as a concrete part of the ui interface and replace them with piles and flows.

jumblerg04:11:00

although both would be of type elem, so to speak, and maybe an elem is the terminal node.

alandipert04:11:07

@jumblerg: are those like columns and rows?

jumblerg04:11:06

no. piles stack elems back to front, over each other. flows lay things out left right top bottom.

jumblerg04:11:02

each could have a direction attribute that could reverse the order as well.

jumblerg04:11:17

the biggest thing we're missing in ui right now is a way to put elements in front or behind of each other in a general way without disturbing the ltrb flow that makes everything intrinsically responsive.

jumblerg04:11:11

we need a notion of a z axis, basically. this is also one of the missing abstractions that has prevented us from implementing controls like dropdown boxes in a general way. or dialogue/modal boxes that appear over a page.

jumblerg04:11:56

a modal, for example, could be done like this:

(pile :s (r 1 1) :p 10 
  (flow :s (r 1 1)
    ...)
  (flow :s (r 1 1) :a :mid :v (cell= (if alert true false)) 
    (flow :s 200 :a :mid :b 2 
      "Alert!")))

onetom04:11:46

interesting separation..

jumblerg04:11:04

the reason we want two different functions is because they take different arguments; gutters, for example, would not apply to piles.

jumblerg04:11:52

piles could also be called stacks, i suppose, but pile seems a bit more irreverent. 🙂

jumblerg04:11:06

maybe there are stronger words found in the vocabulary used by typesetters or something.

onetom04:11:11

btw, if you are looking for inspiration, i highly recommend going thru http://www.rebol.com/docs/view-guide.html 😉

onetom04:11:05

VID (Visual Interface Dialect) is still the most intuitive layout language i've ever seen

jumblerg04:11:18

ah, yes... i remember this.

onetom04:11:13

i mean it was the easiest to learn and keep in mind. it just felt natural. but it was not really meant for resizable layouts, so maybe that's why i can be so simple...

jumblerg04:11:19

there's defininitely some good stuff here. interestingly enough, when i googled flows and stacks just now, qt came up.

onetom04:11:41

and actually i haven't done conditional forms in it, neither multi-step forms and maybe it breaks down on those 😕

onetom04:11:02

talking about qt, i showed you https://gridstylesheets.org in the past; did u have a chance to look into it?

onetom04:11:09

im curious what do u think

jumblerg04:11:41

i did read it, but i never fully digested the cassowary constraint solver thing.

onetom04:11:46

also @laura told me that she was doing more and more swift programming and she really likes their constraint based layout model (and gss is also similar to that)

jumblerg04:11:49

the fact that they're replacing css with gss isn't very promising.

onetom04:11:01

yeah, that's a bit pointless, i agree

jumblerg04:11:16

but there might be some semantics behind these rules worth understanding.

onetom04:11:13

but i was thinking that since we have nice lexical scoping in clojure, so we don't need to come up with silly (gensym) "pointers", maybe the constraint solver could actually get dom elements as parameters...

jumblerg05:11:22

they're losing me with:

#light[years] == 100 !weak;
#light[years] == 200 !medium;
#light[years] == 300 !strong;

jumblerg05:11:40

<ftp://ftp.cs.washington.edu/pub/constraints/papers/constraint-hierarchies-lisp-symb-comp.pdf>

jumblerg05:11:14

we could allow each elem attribute that accepts a numerical value to also accept a set of constraints at some point. the practical value of this is unclear to me right now.

jumblerg05:11:32

it might be way to express some of the functionality we already have more generally. i think this is something to investigate more thoroughly after we get all the basic layout features working across the board.

peterromfeld10:11:15

Hi, i have a question regarding state (we use hoplon/castra) I like to differentiate between ui-state and data-state (data coming from the be to display). Some ppl recently started to modify data-state on the fronend, depending on the result from the be (quoting about pure functions, ie: remove-item, should not return an updated list of all items). It feels quite hard to maintain this approach I usually would just want to have the backend being responsible for data-state (since its usually persisted, ie: remove-item either updates the list of all items on success, or returns error-cell and leaves data-cell as it was -- which is what mkremote is doing) using lenses and formula cells i can also pass muliple data-cells which are effected by a user action. Im not sure which approach is the better one, the latter feels much easier to maintain for me.

dm310:11:03

I know Micha is an advocate of the latter approach. If you are satisfied by the latency/UX of this approach then I'd say you should go with it.

peterromfeld10:11:34

we have much bigger bottlenecks for performance right now, query on db-after after transaction is not even noticeable with the eye on the amount of data we are working with

dm310:11:35

Updating data on the frontend and syncing with the backend is a very hard problem to do generically

jumblerg12:11:09

@peterromfeld: if i understand your query correctly, the latter approach is probably the best place to start: it makes for a more stable system.

jumblerg12:11:08

you can short-circuit the cqrs loop, so to speak, but this is an optimization, and i think the "optimize last" heuristic probably applies here.

peterromfeld12:11:27

thanks for reassuring my thoughts

jumblerg12:11:35

i've found that one of the interesting benefits of working with versioned data, however (for example, if using a db like datomic), is that it becomes relatively safe to do these kinds of optimistic transactions.

jumblerg12:11:09

but in my experience, the benefit is negligible, and may not be worth the trouble. depends on your use case, i guess.

dm312:11:11

if you have occasionally connected clients/concurrent updates - it becomes much harder, but then you can't escape doing stuff on the client anyway

jumblerg12:11:27

@dm3: if your data is versioned, it becomes a lot like syncing git repos. but you can still end up with some nasty complexities; for example, i can imagine a scenario where one transaction is contingent upon some other value that was changed by another client if offline for a significant duration of time. then you have to implement some sort of backtracking scheme to get your client back to the right state, and it becomes unclear to the user what was written and what was not.

jumblerg12:11:06

in a system with any significant amount of complexity, the longer the duration between synchronizations, the greater the odds the client could end up trying to write transactions based on stale data.

peterromfeld12:11:23

thats why you wanna have notify and refresh similiar to pivotal?

peterromfeld12:11:42

was looking very short into it but could not yet figure out, but did not had enough time

jumblerg13:11:22

i'm not sure what pivotal tracker does, but when i was experimenting with optimistic transactions i decoupled the alerts from my views since the user could transition through several views of an application before getting a notification pertaining to some previous write.

jumblerg13:11:11

it needs to be pretty clear to the user when data is being written, and when it is not.

dm313:11:52

@jumblerg you have a few solutions depending of the domain, but you need to merge histories somehow. E.g. OT works well for something like google docs

jumblerg13:11:18

@dm3: yeah, totally. i was about to use them as an example.

dm313:11:34

for most cases just overwriting with the most recent data works well

dm313:11:57

I'm a big fan of expressing the actions explicitly instead of trying to synchronize the dumb data though

dm313:11:21

so anything you do in such an application generates an event

jumblerg13:11:53

yeah. on the other hand, if you wanted to snag shares of AAPL for $100, and then you purchased them for $125 an hour later, then maybe not-so-much.

jumblerg13:11:51

maybe the system causes this super-late transaction to fail, as it should, due to the time difference. but the user might believe he made a small fortune, only to discover later on the purchase never went through. and should the client rewind the user back to the purchase form corresponding to the this failed transaction when the client fails to sync?

jumblerg13:11:56

also, do you settle the lawsuit?

jumblerg13:11:51

so, yeah, @dm3, i think it depends a lot on the domain whether it is worth the trouble.

jumblerg13:11:53

most cases probably not

onetom14:11:48

this problem of late transaction rollback is getting magnified by the various (public) blockchains, where the transaction times are on the several seconds range by default and the global consensus can actually rewrite large portions of the history

onetom14:11:42

that's gonna be tough to write an explanatory UI for such things...

micha15:11:50

patching data state in the frontend is establishing a second level cache, essentially

micha15:11:56

which is a known hard problem

micha15:11:20

i try to avoid it at almost all costs

micha15:11:48

however, there are some things that can be done safely, if they naturally "fail safe"

micha15:11:56

like for example clientside validation

micha15:11:26

i try to avoid doing it, usually, since the backend can be optimized to perform validation as fast as you can ever need

micha15:11:48

but if you do want to do clientside validation you can do that, but with a twist

micha15:11:09

you only use validation results from the client when the result is negative

micha15:11:19

that is, if the clientside validation fails it's ok

micha15:11:32

but if the clientside validation passes i don't use that result

micha15:11:41

and instead wait for the backend validation to pass

micha15:11:06

this way you don't have a situation where the client disagrees with the backend

onetom16:11:13

nice :thumbsup:

onetom16:11:11

btw, does anyone else experience non-updating cljs sources in chrome? it seems it's a long running problem: https://bugs.chromium.org/p/chromium/issues/detail?id=508270

onetom16:11:46

they recommend 2 workarounds: 1. use incognito window 2. emit no-cache headers

micha16:11:56

there is a compiler option to cache-bust them

onetom16:11:00

i wonder how to do the later in boot-jetty... 😕

onetom16:11:15

you mean boot-cljs option?

micha16:11:50

(cljs :compiler-options {:source-map-timestamp true})

micha16:11:19

but the nocache headers seems like a good thing to do anyway

onetom16:11:40

but i dont see how response header modification is possible with boot-jetty

raywillig18:11:17

@onetom isn't there some ring thing to do that?

onetom19:11:59

@raywillig sure, but i don't have any "ring thing" for my frontend project, just a boot-jetty/serve for files in assets/ and on the classpath

raywillig19:11:58

oh right. wasn't thinking. have my mind wrapped around castra stuff right now

raywillig19:11:29

hmm @onetom saw something about a jetty.xml file for config. I wonder if the jetty running in boot-jetty would find it?

micha19:11:58

we could add an option to set those headers in the boot jetty task

micha19:11:03

seems useful

onetom19:11:04

@micha im trying to straighten out our CQRS loops and im wondering how to load inter-dependent state. im trying to chain a sequence with do-watch but every time boot-reload reloads the file, new watches are added but the old ones are not removed. (defonce _ (do-watch ...)) works but of course i have to reload the whole page if i change anything it depends on

onetom19:11:17

iirc you might not have this issue because you always reload the whole page using the :on-load option, no?

micha19:11:18

we should have another arity of that function

micha19:11:28

that lets you provide your own watch key

micha19:11:40

yes i usually reload the page

micha19:11:10

we want like

micha19:11:18

(do-watch ref k init f) arity

onetom19:11:04

i see. indeed that would work..

onetom19:11:37

but while i was playing with this, i've also realized that a lot of the cell graph is regenerated on reload

onetom19:11:59

it means any computation the old formula cells were doing is still done in the background

micha19:11:10

the kind of thinking required to hot patch running code is exactly the kind of thinking that javelin normally saves you from 🙂

onetom19:11:28

hence after 10-20 reloads i'm experiencing serious slowdowns

micha19:11:30

that kind of stateful patching where order is important

micha19:11:18

i think we want some kind of abstraction here

micha19:11:28

to do reloading correctly

micha19:11:34

it's a hard problem

onetom19:11:26

ok, consider this: i have to load the current user first - if any then load some domain object based on what URL am i on - if there is a current user indeed then load more details of that domain object into a modal dialog - which is indicated to be open, also based on the URL, but also based on a property of the domain object

onetom19:11:53

nvm, i will come up with some more tangible minimal example, then it's easier to discuss

laforge4921:11:09

With the latest version of cursive, the ide no longer supports .cljs.hl files--treats them as plain text. 😞

laforge4921:11:40

So I figure, convert to .cljs file. But having problems.

micha21:11:29

what kind of problems?

laforge4921:11:46

reproducing now

laforge4921:11:25

Here's the code

laforge4921:11:59

had to do a boot clean--something is sticky

micha21:11:24

boot clean? is there such a thing?

laforge4921:11:10

no, I'm too distracted

laforge4921:11:38

can't reproduce at the moment--keeps finding the old code which I thought I moved to the css directory.

laforge4921:11:52

really not doing well today--lots of typos

laforge4921:11:05

OK, I'm past the wierd errors. Somehow

laforge4921:11:55

But where is cell defined?

laforge4921:11:15

I tried hoplon/javelin but no go

micha21:11:25

it's in javelin.core

laforge4921:11:50

OK. I had to move my index.cljs file into a namespace, as I was getting a warning with a single-segment namespace.

laforge4921:11:04

Now it just doesn't run the server:

micha21:11:53

not sure what's happening there

micha21:11:04

looks like some issue with core.async?

laforge4921:11:45

If I toggle back to the index.cljs.hl file I get the same warning messages.

micha22:11:29

did you make a html file?

micha22:11:33

to load the javascript?

micha22:11:02

you can add metadata to the namespace to do that

laforge4922:11:13

examples anywhere?

micha22:11:36

(ns ^{:hoplon/page "index.html"} foop.barp
  (:require [some.thing :as thing]))

...

laforge4922:11:43

Thanks again!

micha22:11:46

hm one sec

micha22:11:00

ok yes that's it

laforge4922:11:52

Works great. 🙂

laforge4922:11:13

Now I just need to convert the rest of the files.

laforge4922:11:47

I'm using hoplon.core :as h and it isn't too bad.

laforge4922:11:04

But really nice to have ide support back again.