mirror of
https://github.com/priyanshujain/margin-calendar.git
synced 2026-10-02 11:07:04 +00:00
fix fold wakeup bug
This commit is contained in:
1 parent
9e7d3414d9
commit
c35202674d
5 files changed
+66
-26
No files matched your search
+3
-1
@@ -6,7 +6,9 @@
|
|||||||
machine expects to find applications: `/Applications` on macOS, or the package manager on Linux.
|
machine expects to find applications: `/Applications` on macOS, or the package manager on Linux.
|
||||||
It is the same command whether or not the app is already installed, so it doubles as the update.
|
It is the same command whether or not the app is already installed, so it doubles as the update.
|
||||||
On macOS it asks a running copy to quit first, because replacing a bundle under a live process
|
On macOS it asks a running copy to quit first, because replacing a bundle under a live process
|
||||||
leaves it half old and half new. `just uninstall` reverses it and leaves the data directory alone.
|
leaves it half old and half new, and starts the new one once it is in place, so the copy on screen
|
||||||
|
is never older than the copy installed. `just uninstall` reverses it and leaves the data directory
|
||||||
|
alone.
|
||||||
|
|
||||||
The local build skips the dmg and builds only the `.app`, since nothing about copying a bundle
|
The local build skips the dmg and builds only the `.app`, since nothing about copying a bundle
|
||||||
into place needs a disk image and building one is the slowest part of a mac bundle. That makes a
|
into place needs a disk image and building one is the slowest part of a mac bundle. That makes a
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ build:
|
|||||||
*) echo "just: no local build for $(uname -s); macOS and Linux are the desktop targets." >&2; exit 1 ;;
|
*) echo "just: no local build for $(uname -s); macOS and Linux are the desktop targets." >&2; exit 1 ;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
# Build and install, replacing whatever version is already installed.
|
# Build, install over whatever version is already installed, and start the new one.
|
||||||
install: build
|
install: build
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
@@ -75,6 +75,7 @@ _install-macos:
|
|||||||
cp -R "$src" "$dest"
|
cp -R "$src" "$dest"
|
||||||
version=$(defaults read "$dest/Contents/Info.plist" CFBundleShortVersionString 2> /dev/null || echo "?")
|
version=$(defaults read "$dest/Contents/Info.plist" CFBundleShortVersionString 2> /dev/null || echo "?")
|
||||||
echo "Installed $version to $dest"
|
echo "Installed $version to $dest"
|
||||||
|
open "$dest"
|
||||||
|
|
||||||
_install-linux:
|
_install-linux:
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|||||||
Generated
+1
-1
@@ -2276,7 +2276,7 @@ checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "margin-calendar"
|
name = "margin-calendar"
|
||||||
version = "0.0.4"
|
version = "0.0.5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
"block2",
|
"block2",
|
||||||
|
|||||||
+14
-23
@@ -1,15 +1,15 @@
|
|||||||
// The wall clock, at the two granularities anything on the grid cares about: the minute the now
|
// The wall clock, at the two granularities anything on the grid cares about: the minute the now
|
||||||
// line moves on, and the hour the fit holds open around it.
|
// line moves on, and the hour the fit holds open around it.
|
||||||
//
|
//
|
||||||
// Both schedule off the clock rather than off an interval, so a tab that was asleep and a machine
|
// Both are read off the clock once a minute rather than counted down to. A timer's deadline is
|
||||||
// that woke up catch up on the next tick instead of drifting further out every hour. A timer is
|
// measured in time the machine spent awake, so one set for the top of the hour before the lid
|
||||||
// still a timer, though: one set before the lid closed can fire long after its wall-clock
|
// closed is still waiting out the rest of that hour when it opens, however late it is by then.
|
||||||
// deadline, and a hidden window's timers run late or not at all. So the window coming back, as a
|
// Reading once a minute puts a minute on how far behind either can be; the window coming back, as
|
||||||
// focus or a visibility change, is read as a tick of its own.
|
// a focus or a visibility change, is read as a tick of its own so it is usually less.
|
||||||
//
|
//
|
||||||
// They are separate hooks because they are separate rerenders. The minute belongs to the one
|
// They are separate hooks because they are separate rerenders. The minute belongs to the one
|
||||||
// component that draws the line; putting the fit on that tick would solve the whole axis sixty
|
// component that draws the line. The hour is read as often but only changes once an hour, and a
|
||||||
// times an hour to answer a question that changes once.
|
// state set to the value it already holds rerenders nothing.
|
||||||
|
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
@@ -22,20 +22,12 @@ function hourStart(ms: number): number {
|
|||||||
return d.getTime();
|
return d.getTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The next one. `setMinutes(60)` rolls the date over for us, daylight saving and all. */
|
function useTick<T>(read: (now: number) => T): T {
|
||||||
function nextHourStart(ms: number): number {
|
|
||||||
const d = new Date(ms);
|
|
||||||
d.setMinutes(60, 0, 0);
|
|
||||||
return d.getTime();
|
|
||||||
}
|
|
||||||
|
|
||||||
function useTick<T>(read: (now: number) => T, next: (now: number) => number): T {
|
|
||||||
const [value, setValue] = useState(() => read(Date.now()));
|
const [value, setValue] = useState(() => read(Date.now()));
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let timer = 0;
|
let timer = 0;
|
||||||
const schedule = () => {
|
const schedule = () => {
|
||||||
const now = Date.now();
|
timer = window.setTimeout(tick, MINUTE - (Date.now() % MINUTE));
|
||||||
timer = window.setTimeout(tick, Math.max(0, next(now) - now));
|
|
||||||
};
|
};
|
||||||
const tick = () => {
|
const tick = () => {
|
||||||
window.clearTimeout(timer);
|
window.clearTimeout(timer);
|
||||||
@@ -53,25 +45,24 @@ function useTick<T>(read: (now: number) => T, next: (now: number) => number): T
|
|||||||
document.removeEventListener("visibilitychange", woke);
|
document.removeEventListener("visibilitychange", woke);
|
||||||
window.removeEventListener("focus", woke);
|
window.removeEventListener("focus", woke);
|
||||||
};
|
};
|
||||||
// Both callbacks are module-level functions in every caller, so there is nothing to rebind.
|
// `read` is a module-level function in every caller, so there is nothing to rebind.
|
||||||
}, [read, next]);
|
}, [read]);
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
const now = (ms: number) => ms;
|
const now = (ms: number) => ms;
|
||||||
const nextMinute = (ms: number) => ms + MINUTE - (ms % MINUTE);
|
|
||||||
|
|
||||||
/** Now, refreshed on every wall-clock minute. */
|
/** Now, refreshed on every wall-clock minute. */
|
||||||
export function useMinuteTick(): number {
|
export function useMinuteTick(): number {
|
||||||
return useTick(now, nextMinute);
|
return useTick(now);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The top of the hour it is now, refreshed when the clock turns over into the next one.
|
* The top of the hour it is now, read every minute and changed once an hour.
|
||||||
*
|
*
|
||||||
* Returning the hour rather than the instant is what makes this cheap to depend on: the value is
|
* Returning the hour rather than the instant is what makes this cheap to depend on: the value is
|
||||||
* identical for a whole hour, so a memo keyed on it recomputes once, when the answer changed.
|
* identical for a whole hour, so a memo keyed on it recomputes once, when the answer changed.
|
||||||
*/
|
*/
|
||||||
export function useHourStart(): number {
|
export function useHourStart(): number {
|
||||||
return useTick(hourStart, nextHourStart);
|
return useTick(hourStart);
|
||||||
}
|
}
|
||||||
@@ -280,6 +280,52 @@ test.describe("the hour it is now", () => {
|
|||||||
expect((await axis(page)).map((entry) => entry.text)).not.toContain("11pm");
|
expect((await axis(page)).map((entry) => entry.text)).not.toContain("11pm");
|
||||||
await expect(page.locator(".grid-now")).toHaveCount(0);
|
await expect(page.locator(".grid-now")).toHaveCount(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// The two below are about an app left open, so the clock is a running one rather than a pinned
|
||||||
|
// instant: `install` lets it be moved, and the strip that reads "8pm to 10pm" only exists once
|
||||||
|
// the hour it holds open has moved from nine to ten.
|
||||||
|
test("moves on with the clock while the app stays open", async ({ page }) => {
|
||||||
|
await page.clock.install({ time: clockAt(21, 58) });
|
||||||
|
await openApp(page);
|
||||||
|
await page.clock.pauseAt(clockAt(21, 59));
|
||||||
|
const before = (await axis(page)).map((entry) => entry.text);
|
||||||
|
expect(before).toContain("9pm");
|
||||||
|
expect(before).not.toContain("10pm");
|
||||||
|
|
||||||
|
await page.clock.runFor(2 * 60_000);
|
||||||
|
|
||||||
|
await expect(page.locator(".grid-strip", { hasText: "8pm to 10pm" })).toHaveCount(1);
|
||||||
|
const after = (await axis(page)).map((entry) => entry.text);
|
||||||
|
expect(after).toContain("10pm");
|
||||||
|
expect(after).not.toContain("9pm");
|
||||||
|
await expect(page.locator(".grid-now")).toHaveCount(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("catches up when the clock jumped, as it does after the lid was closed", async ({ page }) => {
|
||||||
|
await page.clock.install({ time: clockAt(21, 30) });
|
||||||
|
await openApp(page);
|
||||||
|
await page.clock.pauseAt(clockAt(21, 31));
|
||||||
|
expect((await axis(page)).map((entry) => entry.text)).toContain("9pm");
|
||||||
|
|
||||||
|
// Asleep, the wall clock moves on and the timers do not: one set for the top of the hour is
|
||||||
|
// still waiting for the rest of its half hour when the machine wakes at five past ten. What
|
||||||
|
// the app gets is one minute of being awake.
|
||||||
|
await page.clock.setSystemTime(clockAt(22, 5));
|
||||||
|
await page.clock.runFor(60_000);
|
||||||
|
|
||||||
|
await expect(page.locator(".grid-strip", { hasText: "8pm to 10pm" })).toHaveCount(1);
|
||||||
|
const hours = (await axis(page)).map((entry) => entry.text);
|
||||||
|
expect(hours).toContain("10pm");
|
||||||
|
expect(hours).not.toContain("9pm");
|
||||||
|
await expect(page.locator(".grid-now")).toHaveCount(1);
|
||||||
|
|
||||||
|
const line = await box(page.locator(".grid-now"));
|
||||||
|
const row = (await gridFit(page)).rowHeight;
|
||||||
|
const ten = (await axis(page)).find((entry) => entry.text === "10pm");
|
||||||
|
const canvas = await box(page.locator(".grid-canvas"));
|
||||||
|
expect(line.top - canvas.top).toBeGreaterThan(ten!.y);
|
||||||
|
expect(line.top - canvas.top).toBeLessThan(ten!.y + row);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test.describe("the all-day band", () => {
|
test.describe("the all-day band", () => {
|
||||||
|
|||||||
Reference in new issue
Block a user