fix(web): the ticket page shows its own changes at once, and a tidier header
The ticket page only refreshed when the live update arrived; a dropped or slow socket left an edit, a new subtask or a comment unseen until the page was reloaded. After each change made there, it now asks for the ticket, its comments and its subtasks again (twice, since the API projects its read model a moment after answering). "Seguir" and "Editar" are two matching buttons, side by side and level with the title's first line (under it on narrow screens), instead of a large button with a stray arrow and a loose grey pencil. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -237,6 +237,41 @@ export const IssueStore = signalStore(
|
||||
debounceTime(150),
|
||||
switchMap(({ payload }) => fetchIssue(payload.issueId)),
|
||||
),
|
||||
/**
|
||||
* What this page just changed, shown without waiting for the live
|
||||
* update, which a dropped socket loses or a slow one delays. The
|
||||
* API projects its read model a moment after answering, so the
|
||||
* ticket, its comments and its subtasks are asked for twice.
|
||||
*/
|
||||
catchUp$: events
|
||||
.on(issueApiEvents.commandSucceeded, issueApiEvents.checklistSaved)
|
||||
.pipe(
|
||||
map(() => store.issue()),
|
||||
filter((issue): issue is Issue => !!issue),
|
||||
switchMap((issue) =>
|
||||
merge(
|
||||
...[300, 1500].map((ms) =>
|
||||
timer(ms).pipe(
|
||||
switchMap(() =>
|
||||
merge(
|
||||
fetchIssue(issue.id),
|
||||
comments
|
||||
.list(issue.id)
|
||||
.pipe(
|
||||
map((list) => issueApiEvents.commentsLoaded(list)),
|
||||
),
|
||||
[
|
||||
issueRealtimeEvents.childrenTouched({
|
||||
issueId: issue.id,
|
||||
}),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
liveComments$: events.on(issuePageEvents.opened).pipe(
|
||||
switchMap(() => loaded$.pipe(map(({ payload }) => payload.id))),
|
||||
filter((id, index) => index === 0 || id !== store.issue()?.id),
|
||||
|
||||
@@ -47,11 +47,15 @@
|
||||
|
||||
.ts-issue__title-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: flex-start;
|
||||
gap: 0.5rem;
|
||||
gap: 0.75rem 1rem;
|
||||
margin-top: 0.75rem;
|
||||
|
||||
h1 {
|
||||
margin: 0.75rem 0 0;
|
||||
flex: 1 1 20rem;
|
||||
min-width: 0;
|
||||
margin: 0;
|
||||
font-size: clamp(1.625rem, 1.2rem + 1.4vw, 2.375rem);
|
||||
font-weight: 700;
|
||||
line-height: 1.1;
|
||||
@@ -59,6 +63,15 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Follow and edit, together, level with the title's first line.
|
||||
.ts-issue__actions {
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
gap: 0.5rem;
|
||||
align-items: center;
|
||||
margin-top: 0.25rem;
|
||||
}
|
||||
|
||||
// Workflow: one button per allowed destination, in that status' colour.
|
||||
.ts-transitions {
|
||||
display: flex;
|
||||
|
||||
@@ -136,29 +136,37 @@ const MAX_FILE_BYTES = 25 * 1024 * 1024;
|
||||
</div>
|
||||
<div class="ts-issue__title-row">
|
||||
<h1>{{ issue.title }}</h1>
|
||||
@if (project.myRole()) {
|
||||
<pi-action-button
|
||||
size="small"
|
||||
color="grey"
|
||||
appearance="outline"
|
||||
[text]="
|
||||
facade.watching()
|
||||
? 'Siguiendo · ' + issue.watchers.length
|
||||
: 'Seguir'
|
||||
"
|
||||
[attr.aria-pressed]="facade.watching()"
|
||||
(execute)="facade.setWatching(!facade.watching())"
|
||||
/>
|
||||
}
|
||||
@if (project.canContribute()) {
|
||||
<pi-icon-button
|
||||
icon="edit"
|
||||
color="grey"
|
||||
appearance="ghost"
|
||||
aria-label="Editar"
|
||||
(execute)="openEdit()"
|
||||
/>
|
||||
}
|
||||
<div class="ts-issue__actions">
|
||||
@if (project.myRole()) {
|
||||
<pi-icon-button
|
||||
[icon]="facade.watching() ? 'eye-off' : 'eye'"
|
||||
color="grey"
|
||||
appearance="outline"
|
||||
[text]="
|
||||
facade.watching()
|
||||
? 'Siguiendo · ' + issue.watchers.length
|
||||
: 'Seguir'
|
||||
"
|
||||
[attr.aria-pressed]="facade.watching()"
|
||||
[attr.title]="
|
||||
facade.watching()
|
||||
? 'Dejar de seguir'
|
||||
: 'Avisarme de sus cambios'
|
||||
"
|
||||
(execute)="facade.setWatching(!facade.watching())"
|
||||
/>
|
||||
}
|
||||
@if (project.canContribute()) {
|
||||
<pi-icon-button
|
||||
icon="edit"
|
||||
color="grey"
|
||||
appearance="outline"
|
||||
text="Editar"
|
||||
title="Editar (tecla E)"
|
||||
(execute)="openEdit()"
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user