-
Notifications
You must be signed in to change notification settings - Fork 27
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
7GUIs Timer #96
Comments
Yes.
Which part exactly is the problem? |
Ok. Then shouldn't it error and not fail silently?
Maybe we can stop with this XY Problem and you can just help me solve for X 😛 It looks behaves like this: https://andreasgruenh.github.io/7guis/#/timer The key difficulty is that the timer stops when it's reached the end, but it can restart if you change the length of the timer. That is, the length of the timer is a Behavior (the scrubber below). Start with the length of time at 5 seconds. Wait 9 seconds. The timer is stuck at 5 seconds. Then increase the timer to 7 seconds. It should now count up from 5 to 7 and then pause again. It shouldn't count the 4 seconds in between hitting the 5 max and you changing the max to 7. It's a lot easier to get if you play with it via the live example link shared above. |
Figured it out! The key insight was that whenever the scrubber changes the https://codesandbox.io/s/o9p4j4p759 const initialMaxTime = 10;
const initialStartTime = Date.now();
const timer = loop(
({ timeChange, maxTime, elapsedTime }) =>
function*() {
yield h1("Timer");
yield span("0");
yield progress({
value: elapsedTime,
max: maxTime
});
yield span(maxTime);
yield div(
elapsedTime.map(s => "Elapsed seconds: " + Math.round(s * 10) / 10)
);
const { input: timeChange_ } = yield input({
type: "range",
min: 0,
max: 60,
value: 10
});
const maxTime_ = yield liftNow(
sample(stepper(initialMaxTime, timeChange.map(e => e.target.value)))
);
const newTimers = snapshotWith(
(a, b) => [a].concat(b),
lift((e, t) => [e, t], elapsedTime, time),
timeChange_.map(e => e.target.value)
).map(([newMaxTime, elapsed, newStart]) =>
time.map(currentT =>
elapsed > newMaxTime
? elapsed
: Math.min(elapsed + currentT / 1000 - newStart / 1000, newMaxTime)
)
);
const elapsedTime_ = yield liftNow(
sample(
switcher(
time.map(t =>
Math.min((t - initialStartTime) / 1000, initialMaxTime)
),
newTimers
)
)
);
return {
timeChange: timeChange_,
maxTime: maxTime_,
elapsedTime: elapsedTime_
};
}
); So while I don't need |
Hi @stevekrouse. That solution looks nice. I think we need to add something to Hareactive to make this part less awkward: const newTimers = snapshotWith(
(a, b) => [a].concat(b),
lift((e, t) => [e, t], elapsedTime, time),
timeChange_.map(e => e.target.value)
).map(([newMaxTime, elapsed, newStart]) =>
time.map(currentT =>
elapsed > newMaxTime
? elapsed
: Math.min(elapsed + currentT / 1000 - newStart / 1000, newMaxTime)
)
); In Hareactive PureScript that can be done a lot easier. We'll need an easier way to snapshot several behaviors at the same time. |
Thanks! Could you show me what this would look like in Hareactive Purescript? (Or is the short answer that it would look like it would in Haskell?) Also, I don't want us to loose track of the fact that |
If f3 <$> b1 <*> b2 <~> s The For TypeScript/JavaScript I was thinking something likes this: liftFoo(f3, [b1, b2], s); That is, Then this code const newTimers = snapshotWith(
(a, b) => [a].concat(b),
lift((e, t) => [e, t], elapsedTime, time),
timeChange_.map(e => e.target.value)
).map(([newMaxTime, elapsed, newStart]) =>
time.map(currentT =>
elapsed > newMaxTime
? elapsed
: Math.min(elapsed + currentT / 1000 - newStart / 1000, newMaxTime)
)
); would become const newTimers = liftFoo(
(elapsed, newStart, newMaxTime) =>
time.map(currentT =>
elapsed > newMaxTime
? elapsed
: Math.min(elapsed + currentT / 1000 - newStart / 1000, newMaxTime)
),
[elapsedTime, time],
timeChange_.map(e => e.target.value)
); which I think is a lot easier to read. |
It's so easy and natural to apply a Here's my proposal: instead of Which would produce: const newTimers = snapshot(
[elapsedTime, time],
timeChange_.map(e => e.target.value)
).map(([newMaxTime, elapsed, newStart]) =>
time.map(currentT =>
elapsed > newMaxTime
? elapsed
: Math.min(elapsed + currentT / 1000 - newStart / 1000, newMaxTime)
)
); |
@stevekrouse I've been thinking a bit about the 7GUI timer a bit. I've created an implementation that I think is very elegant. It features a "Reset" button as well. The logic is pretty much only 2 lines of code and two very simple reusable functions. The secret sauce is
Here is a picture and the complete code. import * as H from "@funkia/hareactive";
import { lift } from "@funkia/jabz";
import { runComponent, modelView, elements as e } from "@funkia/turbine";
const initialMaxTime = 10;
function resetOn(b, reset) {
return b.chain(bi => H.switcher(bi, H.snapshot(b, reset)));
}
function momentNow(f) {
return H.sample(H.moment(f));
}
const timer = modelView(
input =>
momentNow(at => {
const change = lift((max, cur) => (cur < max ? 1 : 0), input.maxTime, input.elapsed);
const elapsed = at(resetOn(H.integrate(change), input.resetTimer));
return { maxTime: input.maxTime, elapsed };
}),
input =>
e
.div([
e.h1("Timer"),
e.span(0),
e.progress({ value: input.elapsed, max: input.maxTime }),
e.span(input.maxTime),
e.div(["Elapsed seconds: ", input.elapsed.map(Math.round)]),
e
.input({ type: "range", min: 0, max: 60, value: initialMaxTime })
.output({ maxTime: "value" }),
e.div({}, e.button("Reset").output({ resetTimer: "click" }))
])
.output(o => ({ elapsed: input.elapsed }))
);
runComponent("#mount", timer()); |
Beautiful! I am a bit lost with Integrating over |
I found the |
I changed the name of this issue to reflect that it's just about the 7GUIs Timer. The other part of this issue, that |
It seems like
changes(time)
orchanges(f(time))
for anyf
doesn't work and fails silently. Is there a problem withchanges()
and continuous time?I'm trying to do this problem and not sure how to do it without this. Should I use
when()
?The text was updated successfully, but these errors were encountered: