Follow-up of #3166.
Current situation
editorActions in packages/web-pkg/src/composables/actions/files/useFileActions.ts maps every entry of appsStore.fileExtensions into a full FileAction. A default instance registers 200+ file extensions, so the computed builds 200+ action objects and rebuilds all of them whenever appsStore.fileExtensions or the embed mode changes.
getAllOpenWithActions then calls isVisible on all 200+ for a single resource. At most one or two of them can ever match, because the last thing isVisible does is compare the resource's extension or mimeType against the one the action was built for.
Every consumer pays this:
getDefaultAction via getResourceLink, once per rendered file list row or tile
triggerDefaultAction on click
- the "Open with" list in
ContextActions.vue and FileActions.vue
Proposal
Build an index from appsStore.fileExtensions once, keyed by
- lowercased
extension
- lowercased
mimeType
- lowercased top-level mime type, so a registration for
image still matches image/jpeg the way isVisible does today
Then look up the one or two candidates for a resource and only evaluate (ideally only construct) actions for those. This turns the per-resource cost from O(number of installed extensions) into O(1).
Things to keep in mind:
- Order matters.
editorActions currently preserves the appsStore.fileExtensions order and then sorts by hasPriority. The index has to preserve both, otherwise the default app for a file type can silently change.
getAllOpenWithActions needs all matching editor actions, not just the first one, for the "Open with" submenu.
- The non-extension parts of
isVisible still have to run per candidate: canDownload()/secureView, the trash location check, the isInVault + external-* app check, and router.hasRoute().
- Constructing actions lazily per candidate would also remove the cost of rebuilding all 209 objects whenever the computed re-evaluates.
appsStore.fileExtensions is also read for icon mapping and the new-file menu. Those consumers are not affected, but may benefit from the same index.
Related
Follow-up of #3166.
Current situation
editorActionsinpackages/web-pkg/src/composables/actions/files/useFileActions.tsmaps every entry ofappsStore.fileExtensionsinto a fullFileAction. A default instance registers 200+ file extensions, so the computed builds 200+ action objects and rebuilds all of them wheneverappsStore.fileExtensionsor the embed mode changes.getAllOpenWithActionsthen callsisVisibleon all 200+ for a single resource. At most one or two of them can ever match, because the last thingisVisibledoes is compare the resource'sextensionormimeTypeagainst the one the action was built for.Every consumer pays this:
getDefaultActionviagetResourceLink, once per rendered file list row or tiletriggerDefaultActionon clickContextActions.vueandFileActions.vueProposal
Build an index from
appsStore.fileExtensionsonce, keyed byextensionmimeTypeimagestill matchesimage/jpegthe wayisVisibledoes todayThen look up the one or two candidates for a resource and only evaluate (ideally only construct) actions for those. This turns the per-resource cost from O(number of installed extensions) into O(1).
Things to keep in mind:
editorActionscurrently preserves theappsStore.fileExtensionsorder and then sorts byhasPriority. The index has to preserve both, otherwise the default app for a file type can silently change.getAllOpenWithActionsneeds all matching editor actions, not just the first one, for the "Open with" submenu.isVisiblestill have to run per candidate:canDownload()/secureView, the trash location check, theisInVault+external-*app check, androuter.hasRoute().appsStore.fileExtensionsis also read for icon mapping and the new-file menu. Those consumers are not affected, but may benefit from the same index.Related