- 
                Notifications
    You must be signed in to change notification settings 
- Fork 833
send alternatives on timeout toast #6920
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
base: main
Are you sure you want to change the base?
Conversation
| Deploying windmill with   | 
| Latest commit: | 0a2d350 | 
| Status: | ✅ Deploy successful! | 
| Preview URL: | https://30f89217.windmill.pages.dev | 
| Branch Preview URL: | https://di-toast-alternatives-on-tim.windmill.pages.dev | 
85561ac    to
    3aded0e      
    Compare
  
    38ccbb0    to
    3bd3862      
    Compare
  
    | Claude finished @diegoimbert's task —— View job Code Review Complete ✅Reviewing PR: send alternatives on timeout toast SummaryThis PR implements a toast notification system that provides actionable alternatives when operations take longer than expected (>4 seconds). The PR includes significant refactoring: extracting the runs page component and audit logs loading logic to use a new  Key Changes: 
 Detailed Findings1. New Utility:  | 
| promise.then((value) => { | ||
| f(value) | ||
| }) | ||
| return promise | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔴 Critical: Potential memory leak and unhandled rejection
The pipe function doesn't handle promise rejections, which will cause unhandled promise rejection warnings. Additionally, it doesn't properly integrate with the cancellation chain.
Suggested fix:
export function pipe<T>(
	promise: CancelablePromise<T>,
	f: (value: T) => void
): CancelablePromise<T> {
	let p = new CancelablePromise<T>((resolve, reject) => {
		promise
			.then((value) => {
				f(value)
				resolve(value)
			})
			.catch((err) => reject(err))
	})
	p.cancel = () => promise.cancel()
	return p
}Alternatively, if this function is not heavily used, consider using finallyDo instead, which already handles this pattern correctly.
| promiseToBeCanceled = promise2 | ||
| return promise2.then((value2) => resolve(value2)).catch((err2) => reject(err2)) | ||
| }) | ||
| .catch((err) => reject(err)) | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The second .catch((err) => reject(err)) is redundant since all errors are already handled in the chain above. The first .catch block either resolves or rejects, so there's no error that would propagate to this second catch.
Consider removing this line for clarity.
| } finally { | ||
| return CancelablePromiseUtils.pure([] as Job[]) | ||
| }) | ||
| CancelablePromiseUtils.pipe(promise, () => { | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ℹ️ Use finallyDo instead of pipe
The pipe function has issues (see comment on its implementation). For side effects that should run regardless of success/failure, use finallyDo:
promise = CancelablePromiseUtils.finallyDo(promise, () => {
	loadingFetch = false
})This ensures proper error handling and cancellation.
| args: | ||
| argFilter && argFilter != '{}' && argFilter != '' && argError == '' ? argFilter : undefined, | ||
| result: | ||
| resultFilter && resultFilter != '{}' && resultFilter != '' && resultError == '' | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This $effect tracks 26 different reactive variables (lines 558-579). Every change to any of these variables will trigger the entire effect to re-run, including onParamChanges() which performs data fetching.
Recommendations:
- Consider splitting into multiple, more focused effects based on related concerns
- Use untrack()more strategically for variables that shouldn't trigger re-fetch
- Group related state into derived objects to reduce the number of dependencies
This is especially important given the TODO comment at the top of the file about refactoring state management.
| type ExtendedJobs, | ||
| OpenAPI | ||
| } from '$lib/gen' | ||
| <!-- TODO : Refactor the runs page to separate state from UI so I don't need to do the {#key trick} --> | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The {#key perPage} workaround (line 11) forces the entire RunsPage component to remount when perPage changes. This is a sign that the component's internal state isn't properly scoped or managed.
Instead of forcing remount, consider:
- Ensuring perPagechanges properly trigger the necessary effects in RunsPage
- Using $effectcleanup functions to cancel ongoing operations when params change
- Better state initialization in RunsPage to handle prop changes
Remounting loses all component state (selections, scroll position, etc.) which might not be the desired UX.
| const extendedMinTs = subtractDaysFromDateString(minTs, lookback) | ||
| if (concurrencyKey == null || concurrencyKey === '') { | ||
| let newJobs = await fetchJobs(maxTs, undefined, extendedMinTs) | ||
| // Extend MinTs to fetch jobs mefore minTs and show a correct concurrency graph | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typographical error: 'mefore' should be 'before'.
| // Extend MinTs to fetch jobs mefore minTs and show a correct concurrency graph | |
| // Extend MinTs to fetch jobs before minTs and show a correct concurrency graph | 

Screen.Recording.2025-10-30.at.15.37.04.mov
Screen.Recording.2025-10-30.at.15.38.18.mov