Office island: persist editor iframe + file list across navigation #165

Open
opened 2026-04-27 07:31:37 +00:00 by zaelgohary · 1 comment
Member

Symptom

Two UX inefficiencies in archipelagos/embed/office:

  1. Editor iframe rebuilds on every doc open. Going List → open Hello.docx → Back → open Goodbye.docx re-bootstraps OnlyOffice's DocsAPI from scratch (~3-5 s of "Loading…"). This is the biggest user-perceptible delay in the office flow.
  2. List view re-fetches on every back-nav. Returning from the editor triggers service::list_documents again, even though nothing changed.

Cause

  1. OfficeApp toggles InnerView::List <-> InnerView::Edit; when Edit exits, the EditorView's iframe is dropped. Next Edit mounts a fresh iframe.
  2. FileListView owns the Resource<Vec<DocumentEntry>>; the resource is dropped on unmount and re-created on remount.

Proposed fix

  1. Keep a single iframe mounted in OfficeApp. On filename change, call editor.destroyEditor() then new DocsAPI.DocEditor(...) on the same <div id="placeholder">. Sub-second swap.
  2. Lift the Resource<Vec<DocumentEntry>> from FileListView into OfficeApp so it persists across the InnerView toggle. Add an explicit "Refresh" handler that the user already has.

Acceptance

  • Re-opening the editor for a different doc completes in <1 s (vs 3-5 s today).
  • Coming back to the list view shows results immediately, not a "Loading…" flicker.
## Symptom Two UX inefficiencies in `archipelagos/embed/office`: 1. **Editor iframe rebuilds on every doc open.** Going List → open Hello.docx → Back → open Goodbye.docx re-bootstraps OnlyOffice's DocsAPI from scratch (~3-5 s of "Loading…"). This is the biggest user-perceptible delay in the office flow. 2. **List view re-fetches on every back-nav.** Returning from the editor triggers `service::list_documents` again, even though nothing changed. ## Cause 1. `OfficeApp` toggles `InnerView::List <-> InnerView::Edit`; when `Edit` exits, the `EditorView`'s iframe is dropped. Next `Edit` mounts a fresh iframe. 2. `FileListView` owns the `Resource<Vec<DocumentEntry>>`; the resource is dropped on unmount and re-created on remount. ## Proposed fix 1. Keep a single iframe mounted in `OfficeApp`. On filename change, call `editor.destroyEditor()` then `new DocsAPI.DocEditor(...)` on the same `<div id="placeholder">`. Sub-second swap. 2. Lift the `Resource<Vec<DocumentEntry>>` from `FileListView` into `OfficeApp` so it persists across the InnerView toggle. Add an explicit "Refresh" handler that the user already has. ## Acceptance - Re-opening the editor for a different doc completes in <1 s (vs 3-5 s today). - Coming back to the list view shows results immediately, not a "Loading…" flicker.
Author
Member

Update: tried two approaches to make the file list survive List ↔ Edit nav. Both fail — the second approach surfaces a Dioxus diff/remount behaviour I do not yet understand.

Attempt 1 — CSS toggle keeps FileListView mounted

let list_display = if in_list { "block" } else { "none" };
rsx! {
    div { style: format!("display: {list_display}; ..."),
        FileListView { context, doc_type, on_open, files: files }
    }
    if let Some(filename) = edit_filename {
        EditorView { ... }
    }
}

Expectation: FileListView never unmounts → use_resource hook persists. Result: still re-fetches on back-nav.

Attempt 2 — Lift use_resource from FileListView into OfficeApp

Resource owned by OfficeApp (which never unmounts during nav), passed as a prop to FileListView. Same use_resource(closure) shape, just at a different level.

Result: still re-fetches on back-nav. Playwright trace:

[t+4878ms] list_documents called  (initial)
[t+9790ms] click back
[t+10035ms] list_documents called  (re-fetch — bug)

Hypothesis

Either OfficeApp is being re-mounted by hero_os's window manager when the inner current signal changes (so all hooks reset), or Dioxus 0.7's use_resource ReactiveContext tracks something subtle that's invalidated by adding/removing the conditional EditorView sibling. Adding key: attributes for stable identity ran into a Dioxus restriction (key: requires interpolation syntax), but is a path worth pursuing.

Next steps for whoever picks this up

  1. Add tracing::info!("OfficeApp render") to the top of the function and confirm whether OfficeApp re-mounts (multiple "first render" traces) or just re-renders.
  2. If it re-mounts: the cause is in hero_os's island_content / window manager, not in the office crate.
  3. If it re-renders: dig into the use_resource ReactiveContext — a memoized endpoint via use_memo may stabilize the captured deps.

Reverting today's local attempt; the live stack on origin/development is unchanged. Issue stays open.

Update: tried two approaches to make the file list survive List ↔ Edit nav. **Both fail** — the second approach surfaces a Dioxus diff/remount behaviour I do not yet understand. ### Attempt 1 — CSS toggle keeps `FileListView` mounted ```rust let list_display = if in_list { "block" } else { "none" }; rsx! { div { style: format!("display: {list_display}; ..."), FileListView { context, doc_type, on_open, files: files } } if let Some(filename) = edit_filename { EditorView { ... } } } ``` Expectation: FileListView never unmounts → `use_resource` hook persists. Result: still re-fetches on back-nav. ### Attempt 2 — Lift `use_resource` from `FileListView` into `OfficeApp` Resource owned by OfficeApp (which never unmounts during nav), passed as a prop to `FileListView`. Same `use_resource(closure)` shape, just at a different level. Result: still re-fetches on back-nav. Playwright trace: ``` [t+4878ms] list_documents called (initial) [t+9790ms] click back [t+10035ms] list_documents called (re-fetch — bug) ``` ### Hypothesis Either OfficeApp is being re-mounted by hero_os's window manager when the inner `current` signal changes (so all hooks reset), or Dioxus 0.7's `use_resource` ReactiveContext tracks something subtle that's invalidated by adding/removing the conditional `EditorView` sibling. Adding `key:` attributes for stable identity ran into a Dioxus restriction (`key:` requires interpolation syntax), but is a path worth pursuing. ### Next steps for whoever picks this up 1. Add `tracing::info!("OfficeApp render")` to the top of the function and confirm whether OfficeApp re-mounts (multiple "first render" traces) or just re-renders. 2. If it re-mounts: the cause is in hero_os's island_content / window manager, not in the office crate. 3. If it re-renders: dig into the `use_resource` ReactiveContext — a memoized endpoint via `use_memo` may stabilize the captured deps. Reverting today's local attempt; the live stack on `origin/development` is unchanged. Issue stays open.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
lhumina_code/hero_archipelagos#165
No description provided.