Replace link-action buttons in the sidebar time-tracker with plain
buttons handled by a delegated click listener in stopwatch.ts. After a
successful POST the two button groups (.issue-start-buttons /
.issue-stop-cancel-buttons) are toggled immediately via show/hide,
so the page never reloads. The navbar icon continues to update via
the WebSocket push or periodic poller as before.
The navbar popup stop/cancel forms used form-fetch-action, which always
reloads the page when the server returns an empty redirect. Remove that
class and add a dedicated delegated submit handler in stopwatch.ts that
POSTs the action silently; the WebSocket push (or periodic poller) then
updates the icon without any navigation.
The stopwatch navbar icon and popup were only rendered by the server
when a stopwatch was already active at page load. If a tab was opened
before the stopwatch started, `initStopwatch()` found no
`.active-stopwatch` element in the DOM, returned early, and never
registered a SharedWorker listener. As a result the WebSocket push
from the stopwatch notifier had nowhere to land and the icon never
appeared.
Fix by always rendering both the icon anchor and the popup skeleton in
the navbar (hidden with `tw-hidden` when no stopwatch is active).
`initStopwatch()` can now set up the SharedWorker in every tab, and
`updateStopwatchData` can call `showElem`/`hideElem` as stopwatch state
changes arrive in real time.
Also add `onShow` to `createTippy` so the popup content is re-cloned
from the (JS-updated) original each time the tooltip opens, keeping
it current even when the stopwatch was started after page load.
Add a new e2e test (`stopwatch appears via real-time push`) that
verifies the icon appears after `apiStartStopwatch` is called with
the page already loaded.
Add e2e tests for the three server push features:
- **Notification count**: verifies badge appears when another user
creates an issue
- **Stopwatch**: verifies stopwatch element is rendered when a stopwatch
is active
- **Logout propagation**: verifies logout in one tab triggers redirect
in another
Tests are transport-agnostic in preparation for a future WebSocket
migration.
---------
Co-authored-by: Claude (Opus 4.6) <noreply@anthropic.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
`import.meta.env` is supported in both vitest and webpack [as of
recent](https://github.com/webpack/webpack/pull/19996), so replace all
previous use of `process.env` with it. Current usage is limited to test
files, I've also verified it works in actual frontend code.
`webpack/module` is added to typescript types which includes the
definition for `import.meta.env`. I've also made the eslint globals more
precise. Finally, `__webpack_public_path__` is removed from our type
definitions because `webpack/module` also provides it.
This PR fixes a state de-synchronization bug with the issue stopwatch,
it resolves the issue by replacing the ambiguous `/toggle` endpoint
with two explicit endpoints: `/start` and `/stop`.
- The "Start timer" button now exclusively calls the `/start` endpoint.
- The "Stop timer" button now exclusively calls the `/stop` endpoint.
This ensures the user's intent is clearly communicated to the server,
eliminating the state inconsistency and fixing the bug.
---------
Signed-off-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Estimated time represented in hours it might be convenient to
have tracked time represented in the same way to be compared and
managed.
---------
Co-authored-by: Sysoev, Vladimir <i@vsysoev.ru>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Adds types to various low-level modules. All changes are type-only, no
runtime changes. `tsc` now reports 38 less errors.
One problem was that `@types/sortablejs` does not accept promise return
in its functions which triggered the linter, so I disabled the rules on
those line.
None of the frontend js/ts files was touched besides these two commands
(edit: no longer true, I touched one file in
61105d0618
because of a deprecation that was not showing before the rename).
`tsc` currently reports 778 errors, so I have disabled it in CI as
planned.
Everything appears to work fine.