Skip to content

Improve workspace page browsing#16

Merged
Chefski merged 5 commits into
devfrom
improve-page-browsing-ui
Jul 6, 2026
Merged

Improve workspace page browsing#16
Chefski merged 5 commits into
devfrom
improve-page-browsing-ui

Conversation

@Chefski

@Chefski Chefski commented Jul 5, 2026

Copy link
Copy Markdown
Owner

Summary

  • add a top workspace page switch for recently updated, favorites, and created-by-me pages
  • increase page browsing row spacing and add larger scan-friendly rows
  • add the workspace-logo menu on the Docmostly root screen with Settings and Log Out
  • wire the native client to Docmost pages/created-by-user and cover the new tab loader

Verification

  • git diff --check
  • swiftlint lint --strict --no-cache
  • XcodeBuildMCP build_run_sim on iPhone 17 Pro, iOS 26.5, with -MainShellPreview
  • XcodeBuildMCP snapshot_ui verified page switch and workspace menu
  • XcodeBuildMCP test_sim -only-testing:docmostlyTests/EngagementEndpointTests -only-testing:docmostlyTests/PageBrowserViewModelTests

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

No issues found across 8 files

Re-trigger cubic

@greptile-apps

greptile-apps Bot commented Jul 5, 2026

Copy link
Copy Markdown

Greptile Summary

This PR adds scope-based workspace page browsing (Recently Updated, Favorites, Created by Me) to PageTreeView, wires a new pages/created-by-user endpoint with offline fallback, increases iOS list-row spacing, and adds a workspace-logo account menu (Settings / Log Out) to the sidebar root. The changes are well-decomposed into separate Swift files in line with the project guide, and all previously flagged issues (stale updatedAt on favorites, resolvedUserID not propagating to the API call, multiple types in one file) are addressed.

  • Scope switch: PageBrowserViewModel drives a three-tab page browser backed by PageBrowserProviding; pageBrowserTaskKey causes SwiftUI to restart the load task whenever the space or scope changes, with activeLoadID guarding against stale responses.
  • New endpoint: Endpoint.createdByUser follows the same encode pattern as recentPages; loadCreatedByPages uses resolvedUserID throughout both the live and offline paths.
  • Workspace menu: WorkspaceAccountMenu replaces the old Settings sidebar link with a logo button that also exposes Log Out.

Confidence Score: 5/5

Safe to merge; scope-switching and the new endpoint are well-guarded against races and offline conditions, all previously flagged issues are resolved, and unit tests cover all three scopes.

The loadCreatedByPages network path now correctly uses resolvedUserID, favorites updatedAt is properly set to nil, and every new type is in its own file. The only remaining note is a brief flash of stale list items when switching scopes, which is cosmetic and does not affect data correctness.

docmostly/Features/PageTree/PageBrowserViewModel.swift — a one-line fix to clear items at the start of load() would eliminate the stale-content flash during scope switching.

Important Files Changed

Filename Overview
docmostly/App/AppState+Engagement.swift Adds loadCreatedByPages() with correct resolvedUserID usage throughout (API path and both offline paths), plus private createdCachedPages() helper; addresses previous review concerns.
docmostly/Features/PageTree/PageBrowserViewModel.swift New @MainActor @Observable view model orchestrating scope-based page loads; items is not cleared when a new load begins, so stale items from the previous scope are briefly visible alongside the new scope's loading indicator.
docmostly/Features/PageTree/PageTreeView.swift Integrates PageBrowserViewModel and scope switch UI, retains navigationDestination(for: PageTreeNode.self) so iOS tree navigation still works; PageTreeSpaceActionsMenu correctly extracted to its own file.
docmostly/Features/PageTree/PageBrowserItem.swift Clean nonisolated Sendable value type; favorites path correctly sets updatedAt = nil (no updatedAt on DocmostFavoritePage), resolving the previous review concern about bookmark-creation-time being shown as page-modification-time.
docmostly/Features/PageTree/PageBrowserProviding.swift Well-defined @MainActor protocol with AppState extension; cleanly separates the provider contract from the view model.
docmostly/Features/Spaces/WorkspaceAccountMenu.swift New workspace-logo menu with Settings and Log Out; proper use of Task { await appState.logout() } for the async logout action.
docmostly/Networking/Endpoint.swift Adds createdByUser endpoint and CreatedByUserRequest struct following the exact same pattern as the adjacent recentPages endpoint.
docmostlyTests/PageTree/PageBrowserViewModelTests.swift Well-structured tests with a proper spy covering all three scopes; verifies updatedAt == nil for favorites, correct userId forwarding for createdByMe, and filtering of favorites without a page.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[User taps scope tab] --> B[selectedScope changes]
    B --> C[pageBrowserTaskKey recomputes]
    C --> D[SwiftUI cancels old task, starts new .task]
    D --> E[refreshBrowser calls browserViewModel.load]
    E --> F[isLoading = true, items cleared, errorMessage = nil]
    F --> G{apiClient available?}
    G -- No --> H[createdCachedPages / recentCachedPages / throw]
    G -- Yes --> I[API call: loadRecentPages / loadFavorites / loadCreatedByPages]
    I -- Success --> J{activeLoadID == loadID AND selectedScope == requestedScope?}
    I -- Error --> K{canUseOfflineCache?}
    K -- Yes --> H
    K -- No --> L[throw error]
    J -- No --> M[Discard result, return]
    J -- Yes --> N[items = loadedItems, isLoading = false]
    H --> N
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A[User taps scope tab] --> B[selectedScope changes]
    B --> C[pageBrowserTaskKey recomputes]
    C --> D[SwiftUI cancels old task, starts new .task]
    D --> E[refreshBrowser calls browserViewModel.load]
    E --> F[isLoading = true, items cleared, errorMessage = nil]
    F --> G{apiClient available?}
    G -- No --> H[createdCachedPages / recentCachedPages / throw]
    G -- Yes --> I[API call: loadRecentPages / loadFavorites / loadCreatedByPages]
    I -- Success --> J{activeLoadID == loadID AND selectedScope == requestedScope?}
    I -- Error --> K{canUseOfflineCache?}
    K -- Yes --> H
    K -- No --> L[throw error]
    J -- No --> M[Discard result, return]
    J -- Yes --> N[items = loadedItems, isLoading = false]
    H --> N
Loading

Reviews (5): Last reviewed commit: "Avoid full tree reloads on page filter c..." | Re-trigger Greptile

Comment thread docmostly/App/AppState+Engagement.swift
icon = favorite.page?.icon
spaceId = favorite.page?.spaceId ?? favorite.spaceId
subtitle = fallbackSpaceName
updatedAt = favorite.createdAt

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Favorites updatedAt reflects favorited-at, not page updated-at

updatedAt is set to favorite.createdAt — the moment the user saved the favorite — rather than when the page itself was last changed. PageBrowserRowView surfaces this date unlabelled next to the space name, so users scanning the Favorites list will read "Yesterday" as the page modification time when it is actually the bookmark creation time. Either use favorite.page?.updatedAt (when available) or omit the date entirely for the favorites path.

Suggested change
updatedAt = favorite.createdAt
updatedAt = favorite.page?.updatedAt ?? favorite.createdAt

Comment thread docmostly/Features/PageTree/PageBrowserViewModel.swift Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ecc137c04f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +23 to +26
ForEach(browserViewModel.items) { item in
NavigationLink(value: item) {
PageBrowserRowView(item: item)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Restore the full page tree in spaces

In the iOS/iPad space content route, MainShellContentView still instantiates PageTreeView, but this view now renders only browserViewModel.items from the recent/favorites/created-by-me loaders and no longer calls loadRoot; I also checked that the remaining PageTreeNodeView usage is macOS-only. This means any accessible page in the selected space that is not recent, favorited, or created by the current user disappears from the space browser, so users can no longer browse much of a workspace's nested page hierarchy from iPhone/iPad.

Useful? React with 👍 / 👎.

cursor: nil,
limit: Self.defaultPageLimit
)
items = response.items.map { PageBrowserItem(page: $0, fallbackSpaceName: space.name) }

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Ignore stale browser loads

When the user switches from Recently Updated to Favorites/Created by Me before the recent request returns, this assignment still writes the older response after the await. PageTreeView cancels and restarts the task on scope changes, but AppState treats non-HTTP errors such as cancellation as cache-eligible, so the canceled recent load can still return cached/recent items and overwrite the newly selected tab. Capture the requested scope/token and ignore cancellation or stale completions before mutating items.

Useful? React with 👍 / 👎.

moveToSpace: beginMoveToSpace,
delete: deletePage
ForEach(browserViewModel.items) { item in
NavigationLink(value: item) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve page row management actions

On iOS/iPad, these rows now use PageBrowserRowView instead of PageTreeNodeView, but the new row has no context menu or callbacks for New subpage, Duplicate, Move to Space, or Move to Trash. I checked the page/space feature files and the only remaining menu with those actions is on PageTreeNodeView, which is now only reached from the macOS sidebar, so visible pages in the mobile/tablet space browser can no longer be managed from their rows.

Useful? React with 👍 / 👎.

@greptile-apps

greptile-apps Bot commented Jul 5, 2026

Copy link
Copy Markdown

Want your agent to iterate on Greptile's feedback? Try greploops.

Comment thread docmostly/App/AppState+Engagement.swift
@Chefski Chefski merged commit c11f595 into dev Jul 6, 2026
8 checks passed
@Chefski Chefski deleted the improve-page-browsing-ui branch July 6, 2026 01:07
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.

1 participant