Skip to content

web: allow blocking pages on certain progress signals#2947

Merged
dgdavid merged 12 commits intoapi-v2from
api-v2-block-on-progress
Dec 17, 2025
Merged

web: allow blocking pages on certain progress signals#2947
dgdavid merged 12 commits intoapi-v2from
api-v2-block-on-progress

Conversation

@dgdavid
Copy link
Copy Markdown
Contributor

@dgdavid dgdavid commented Dec 16, 2025

A few weeks ago, in #2909, a small, non-blocking component was introduced to subtly inform users when actions are in progress . This PR introduces its blocking counterpart, allowing Agama pages to subscribe to specific progress signals and block user interactions until those progress events are complete.

The changes in this PR ended up being more complex than initially expected due to an edge case that caused an undesirable flickering effect: the progress indicator would disappear almost simultaneously, or even slightly before, certain proposal queries were invalidated and refetched. This led to a transition from the progress state to an outdated UI, which was not ideal.

To address this, a new hook has been implemented to subscribe to the TanStack Query cache, notifying when specific queries are considered fully refetched after a certain point. This is then used in conjunction with useProposalChanges query invalidation. As a result, it now emits a custom event once proposal-related queries are fully refetched, allowing the Page component to subscribe to this event and unmount the blocking overlay just before React performs the UI redraw.

Please, note that using something like const { isFetching } = useProposalChanges() wasn't suitable for this scenario. For further details, please refer to the code and commit messages.

localhost_8080_(iPad Mini) (9)

Additional notes

  • Currently, only the software pages (all of them) and the storage proposal page are subscribed to their respective progress scopes. The only pages missing at the moment are the network pages, but since the progress for network is not yet ready, TypeScript raises an error when trying to include these pages in this PR, as it doesn't recognize "network" as a valid scope.

  • It would be nice to allow pages to conditionally discard the blocking state based on the number of progress steps. This idea originated with the assumption that one-step progress (like for storage proposals) would be fast, though it might not be for certain users or stacks. It's difficult to predict since other factors such as connection speed, whether the page is remote or localhost, etc can have a significant impact. These factors could be explored further to refine the behavior, if/when time permits.

  • Another possibility, which is fully outside the scope of this PR for now because there is no current use case, is to allow pages to subscribe to multiple progress scopes, rather than just one.

TanStack Query does not provide a built-in way to notify when queries
have finished refetching after being invalidated. The library author
usually recommends relying on `useQuery` to react to query state
changes, which makes sense in most cases. However, in Agama there is a
use case that can be considered an exception to this rule, where
components should not directly depend on query state but only be
notified when their data is considered fresh after a certain moment.

This is the case for the core/Page component, which will be changed to
enter a "loading" state when receiving a signal from the Agama progress
API. After progress completion, a proposal update signal may be
received, causing related queries to be invalidated. At that point,
TanStack Query triggers a refetch, and the core/Page component must wait
until all affected queries have fully refetched before exiting the
loading state.

Exiting the loading state as soon as progress ends can result in
rendering stale data, which is then replaced moments later, causing
noticeable UI flicker. This hook allows tracking refetch completion so
the loading state is exited only when data is fresh.
Adapt the `useProposalChanges` hook to use the previously introduced
refetch-tracking hook, allowing it to detect when all involved queries
have finished refetching after being invalidated.

Once the refetch completes, a DOM-based event is emitted so that other
parts of Agama can react accordingly. This will be primarily used by the
core/Page component to exit the loading state gracefully and at the
correct time.
PF/PageSectionProps and PF/PageGroupProps already define children; no
need to wrap them with React.PropsWithChildren.
Work in progress adapting the core/Page component so it can enter a
loading state while a progress of a given scope is active.

This commit is intentionally sent as WIP to allow others to experiment
with it while styles are being refactored.

For now, only storage/ProposalPage is adapted to subscribe to the
progress scopes it is interested in.
This commit continues the work from the previous one, further refining
the core/Page component by removing unnecessary code and extracting the
progress overlay logic into an internal component.

Additionally, inline styles are replaced with a CSS class.
The tests were fixed by replacing `plainRender` with
`installerRender`where needed and reordering imports to ensure that
`test-utils` is imported before the component, allowing the centralized
react-routter mocks to work as expected.  Including `test-utils.tsx` in
`jest.config.js#setupFilesAfterEnv` was because it caused the
`ProductRegistrationAlertMock` to fail for unknown reasons.
Refactors how ProgressBackdrop  handle state transitions during data refresh
and adds few tests for it.
Although the use of useTrackQueriesRefetch worked well with the
QueryObservers approach, using a subscription to the queryCache with the
necessary filtering is considered a better practice. As a result, the
hook has been updated to use this approach, while ensuring that all
tests continue to pass.

More info:

 * https://tkdodo.eu/blog/inside-react-query
 * https://tanstack.com/query/latest/docs/reference/QueryCache
 * TanStack/query#3292
@dgdavid dgdavid changed the title [WIP] web: allow blocking pages on progress web: allow blocking pages on certain progress signals Dec 17, 2025
@dgdavid dgdavid marked this pull request as ready for review December 17, 2025 14:29
Comment on lines +156 to +160
if (
query.state.status === "success" &&
query.state.dataUpdatedAt > startedAt &&
!refetchedKeys.has(queryKey)
) {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe I'm misunderstanding something, but using query.state.fetchStatus, query.isStale, or similar properties to directly check here if a query is in a refetching state ended up making things more complex for this scenario IMHO. The hook would need to track multiple statuses transitions (e.g., from stale/invalidated to fetching, and then to success), which increases the complexity.

Instead, working with the dataUpdatedAt timestamp feels simpler and enought for this use case, based on my current understanding.

It would be nice if the library had a built-in way to subscribe to query refetches directly.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice if the library had a built-in way to subscribe to query refetches directly.

I mean, a "decoupled" mechanism other than useQuery for use cases similar we have faced here. . The author argues that useQuery usage should be enough in most cases, but in situations like this, it feels like a more specialized solution would be useful.

Copy link
Copy Markdown
Contributor

@imobachgs imobachgs left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just some typos. Otherwise, it LGTM.

dgdavid and others added 2 commits December 17, 2025 16:43
Co-authored-by: Imobach González Sosa <igonzalezsosa@suse.com>
@dgdavid dgdavid requested a review from imobachgs December 17, 2025 16:51
@dgdavid dgdavid merged commit 30bf251 into api-v2 Dec 17, 2025
7 of 9 checks passed
@dgdavid dgdavid deleted the api-v2-block-on-progress branch December 17, 2025 17:07
dgdavid added a commit that referenced this pull request Dec 18, 2025
Initially, internal `ProgressBackdrop` component at `core/Page`, relied
on a global event system to detect when proposal-related queries
finished refetching in order to consider an ongoing progress in the
given scope was finished (check
#2947). While this worked on
the storage page, it failed on other pages becuae the _central_
`useProposalChanges` hook is invalidating an storage-related query,
which is not refetched until navigating to a storage-related page. As a
result, the progress backdrop could remain stuck on unrelated storage
pages.

This commit refactors the tracking mechanism to make it more flexible
and page-aware:

* Start exporting query keys as constants (COMMON_PROPOSAL_KEYS,
STORAGE_MODEL_KEY, etc.) from their respective modules

* Add the `additionalProgressKeys` prop to Page, allowing pages to
specify extra query keys to track alongside the common proposal-related
queries

* Use `useTrackQueriesRefetch` directly into ProgressBackdrop, dropping
the need for a custom global event system

* Remove the proposal:updated event infrastructure (onProposalUpdated,
dispatchProposalUpdated), which is no longer required

With this change, pages can explicitly declare which additional queries
must complete refetching before the UI is unblocked. For example, the
storage `ProposalPage` now waits for the `storageModel` query in
addition to the common proposal queries.

While requiring pages to be explicit about the queries they depend on is
not ideal, this preserves common proposal tracking as the default while
preventing the UI from remaining blocked due to unrelated query
invalidations.
imobachgs added a commit that referenced this pull request Jan 10, 2026
Merge the new HTTP API. Each PR has been already reviewed, so it should
be safe to merge it.

* #1829
* #2508
* #2772
* #2826
* #2848
* #2860
* #2863
* #2866
* #2867
* #2869
* #2870
* #2871
* #2872
* #2873
* #2874
* #2875
* #2876
* #2877
* #2880
* #2881
* #2882
* #2884
* #2885
* #2886
* #2891
* #2892
* #2893
* #2894
* #2895
* #2896
* #2897
* #2898
* #2899
* #2900
* #2901
* #2902
* #2903
* #2904
* #2908
* #2909
* #2910
* #2912
* #2913
* #2914
* #2915
* #2916
* #2917
* #2918
* #2920
* #2921
* #2923
* #2924
* #2926
* #2928
* #2929
* #2930
* #2933
* #2934
* #2935
* #2936
* #2938
* #2939
* #2942
* #2943
* #2944
* #2945
* #2946
* #2947
* #2948
* #2950
* #2951
* #2952
* #2954
* #2955
* #2956
* #2957
* #2958
* #2959
* #2960
* #2961
* #2963
* #2964
* #2965
* #2967
* #2968
* #2969
* #2970
* #2971
* #2972
* #2974
* #2975
* #2977
* #2978
* #2980
* #2982
* #2983
* #2984
* #2988
* #2989
* #2991
* #2992
* #2993
* #2994
* #2995
* #2996
* #2997
* #2999
joseivanlopez added a commit that referenced this pull request Mar 3, 2026
)

As a follow up of #3143,
restores the DASD format progress by adapting `DASDFormatProgress` to
the new API v2 event model, using `DASDFormatChanged` websocket events
to drive progress state and only rendering when formatting is actually
active.

To support injecting the per-device progress details into the existing
backdrop overlay, `ProgressBackdrop` was extended with an `extraContent`
prop, which can be used by pages to inject any extra content of their
convenience to the progress overlay.

This PR was also taken as an opportunity to improve the readability of
the progress overlay, on hold since
#2947, by wrapping its
content in a Card layout, which provides a clear visual container,
structured spacing, and scrollable content for long step messages.

A `waitingLabel` prop was added as another minor improvement to let
consumers override the default `"Refreshing data..."` message shown
while queries finish refetching.
bmwiedemann pushed a commit to bmwiedemann/openSUSE that referenced this pull request Mar 11, 2026
https://build.opensuse.org/request/show/1338003
by user IGonzalezSosa + anag_factory
- Remove code to deal with the old HTTP API
  (gh#agama-project/agama#3258).

- Allow downloading the user config (jsc#PED-15453).

- Improve registration and software issues reporting (related to bsc#1258034).

- Drop vulnerable dev dependency no longer needed (bsc#1259117).

- Restores DASD format progress with API v2 event model
  (gh#agama-project/agama#3143).
- Improves progress backdrop layout and readbility
  (gh#agama-project/agama#2947).

- Add visual feedback on table row hover and focus
  (gh#agama-project/agama#3233).

- Update web dependencies to their latest available versions
  as of 2026-03-02 (gh#agama-project/agama#3232).

- Do not reset the configuration on product selection (bsc#1258032).

- Do not show empty software page (related to bsc#1258922).

- Restore the interfa
@imobachgs imobachgs mentioned this pull request Mar 17, 2026
imobachgs added a commit that referenced this pull request Mar 17, 2026
Prepare to release version 19.

* #1829
* #2508
* #2772
* #2818
* #2826
* #2848
* #2860
* #2863
* #2864
* #2866
* #2867
* #2869
* #2870
* #2871
* #2872
* #2873
* #2874
* #2875
* #2876
* #2877
* #2880
* #2881
* #2882
* #2884
* #2885
* #2886
* #2891
* #2892
* #2893
* #2894
* #2895
* #2896
* #2897
* #2898
* #2899
* #2900
* #2901
* #2902
* #2903
* #2904
* #2908
* #2909
* #2910
* #2912
* #2913
* #2914
* #2915
* #2916
* #2917
* #2918
* #2920
* #2921
* #2923
* #2924
* #2926
* #2928
* #2929
* #2930
* #2933
* #2934
* #2935
* #2936
* #2937
* #2938
* #2939
* #2942
* #2943
* #2944
* #2945
* #2946
* #2947
* #2948
* #2949
* #2950
* #2951
* #2952
* #2954
* #2955
* #2956
* #2957
* #2958
* #2959
* #2960
* #2961
* #2963
* #2964
* #2965
* #2967
* #2968
* #2969
* #2970
* #2971
* #2972
* #2974
* #2975
* #2977
* #2978
* #2980
* #2981
* #2982
* #2983
* #2984
* #2988
* #2989
* #2990
* #2991
* #2992
* #2993
* #2994
* #2995
* #2996
* #2997
* #2998
* #2999
* #3000
* #3001
* #3002
* #3004
* #3005
* #3006
* #3007
* #3008
* #3009
* #3011
* #3012
* #3013
* #3014
* #3015
* #3016
* #3018
* #3019
* #3020
* #3021
* #3022
* #3023
* #3024
* #3025
* #3026
* #3027
* #3028
* #3029
* #3030
* #3031
* #3033
* #3034
* #3035
* #3036
* #3037
* #3039
* #3040
* #3041
* #3042
* #3043
* #3044
* #3045
* #3046
* #3047
* #3048
* #3049
* #3050
* #3051
* #3052
* #3053
* #3054
* #3055
* #3056
* #3057
* #3058
* #3060
* #3061
* #3062
* #3063
* #3064
* #3065
* #3066
* #3067
* #3068
* #3069
* #3070
* #3071
* #3072
* #3073
* #3074
* #3075
* #3076
* #3077
* #3078
* #3079
* #3086
* #3087
* #3088
* #3089
* #3090
* #3091
* #3092
* #3093
* #3094
* #3095
* #3096
* #3097
* #3098
* #3099
* #3100
* #3101
* #3102
* #3103
* #3104
* #3105
* #3106
* #3107
* #3108
* #3109
* #3110
* #3112
* #3113
* #3114
* #3115
* #3116
* #3117
* #3118
* #3119
* #3120
* #3122
* #3123
* #3124
* #3127
* #3128
* #3129
* #3130
* #3131
* #3133
* #3134
* #3135
* #3136
* #3137
* #3138
* #3139
* #3140
* #3141
* #3142
* #3143
* #3144
* #3145
* #3146
* #3147
* #3148
* #3149
* #3150
* #3151
* #3152
* #3153
* #3154
* #3155
* #3157
* #3158
* #3159
* #3160
* #3161
* #3162
* #3163
* #3164
* #3165
* #3166
* #3167
* #3168
* #3169
* #3170
* #3174
* #3175
* #3176
* #3177
* #3178
* #3179
* #3181
* #3182
* #3184
* #3185
* #3186
* #3188
* #3189
* #3190
* #3191
* #3192
* #3194
* #3195
* #3196
* #3197
* #3198
* #3199
* #3200
* #3201
* #3202
* #3203
* #3205
* #3206
* #3208
* #3209
* #3210
* #3213
* #3214
* #3215
* #3216
* #3217
* #3218
* #3219
* #3220
* #3222
* #3223
* #3224
* #3225
* #3226
* #3227
* #3228
* #3229
* #3230
* #3231
* #3232
* #3233
* #3234
* #3235
* #3236
* #3237
* #3238
* #3239
* #3240
* #3241
* #3242
* #3243
* #3244
* #3246
* #3247
* #3248
* #3250
* #3251
* #3252
* #3253
* #3254
* #3255
* #3256
* #3257
* #3258
* #3259
* #3260
* #3261
* #3262
* #3263
* #3265
* #3266
* #3267
* #3268
* #3269
* #3270
* #3271
* #3272
* #3273
* #3274
* #3275
* #3276
* #3277
* #3278
* #3279
* #3280
* #3281
* #3282
* #3283
* #3284
* #3285
* #3286
* #3287
* #3288
* #3289
* #3290
* #3291
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants