Skip to content

Conversation

@appflowy
Copy link
Contributor

@appflowy appflowy commented Nov 26, 2025

Description


Checklist

General

  • I've included relevant documentation or comments for the changes introduced.
  • I've tested the changes in multiple environments (e.g., different browsers, operating systems).

Testing

  • I've added or updated tests to validate the changes introduced for AppFlowy Web.

Feature-Specific

  • For feature additions, I've added a preview (video, screenshot, or demo) in the "Feature Preview" section.
  • I've verified that this feature integrates seamlessly with existing functionality.

Summary by Sourcery

Improve publish namespace handling and share popover behavior, and align local dev and deployment behavior for namespace-based routing and logging.

New Features:

  • Open the publish management panel in a dedicated modal triggered from the share popover, with proper test coverage.

Bug Fixes:

  • Ensure opening publish management from the namespace caret closes the share popover first to avoid overlapping UI states.
  • Align dev-time namespace redirects with production behavior so bare-namespace URLs redirect to the default published page where available.
  • Treat localhost as an official hostname regardless of port when checking subscription hostnames.

Enhancements:

  • Refine publish metadata logging on the deploy server, including configurable log level, less noisy fetch logs, and clearer error and fallback messages.

Build:

  • Add a Vite dev server middleware plugin to perform namespace-based redirects mirroring the production deploy server logic.

Deployment:

  • Improve deploy/server logging behavior and fallback handling when metadata fetch or namespace redirects fail.

Tests:

  • Extend Cypress publish-page tests and selectors to cover opening and closing the publish management modal from the namespace caret.

@sourcery-ai
Copy link

sourcery-ai bot commented Nov 26, 2025

Reviewer's Guide

Refactors how publish management is opened from the share popover by moving the PublishManage modal to ShareButton, wiring it through ShareTabs/PublishPanel/PublishLinkPreview, adds a dev-time namespace redirect middleware matching server behavior, tightens server logging and error handling for namespace metadata, adjusts official hostname handling, and extends Cypress tests/selectors for the namespace popover and publish manage flow.

Sequence diagram for opening publish manage modal from share popover

sequenceDiagram
  actor User
  participant ShareButton
  participant Popover
  participant ShareTabs
  participant PublishPanel
  participant PublishLinkPreview
  participant NormalModal
  participant PublishManage

  User->>ShareButton: click share button
  ShareButton->>Popover: open = true
  Popover->>ShareTabs: render with opened=true, onOpenPublishManage
  ShareTabs->>PublishPanel: render active Panel with onOpenPublishManage
  PublishPanel->>PublishLinkPreview: render with onOpenPublishManage

  User->>PublishLinkPreview: click open-publish-settings button
  PublishLinkPreview->>PublishLinkPreview: onClose()
  PublishLinkPreview->>ShareTabs: onOpenPublishManage()
  ShareTabs->>ShareButton: call onOpenPublishManage
  ShareButton->>Popover: setOpened(false)
  ShareButton->>NormalModal: setPublishManageOpen(true)

  NormalModal->>PublishManage: render
  User->>NormalModal: press Escape key
  NormalModal->>ShareButton: onClose()
  ShareButton->>NormalModal: setPublishManageOpen(false)
  NormalModal-->>PublishManage: unmount

  Note over Popover,NormalModal: Share popover is closed before
  Note over NormalModal,PublishManage: PublishManage lives outside popover in a top-level modal
Loading

Class diagram for updated share and publish management components

classDiagram
  class ShareButton {
    +string viewId
    -boolean opened
    -boolean publishManageOpen
    +ShareButton(props)
  }

  class ShareTabs {
    -boolean opened
    -string viewId
    -function onClose
    -function onOpenPublishManage
    -TabKey value
    -Array options
    +ShareTabs(opened, viewId, onClose, onOpenPublishManage)
  }

  class PublishPanel {
    -string viewId
    -boolean opened
    -function onClose
    -function onOpenPublishManage
    +PublishPanel(viewId, opened, onClose, onOpenPublishManage)
  }

  class PublishLinkPreview {
    -string viewId
    -object publishInfo
    -string url
    -boolean loading
    -boolean isOwner
    -boolean isPublisher
    -function onClose
    -function onOpenPublishManage
    -boolean renameOpen
    -string publishName
    +PublishLinkPreview(viewId, publishInfo, url, loading, isOwner, isPublisher, onClose, onOpenPublishManage)
  }

  class PublishManage {
    -function onClose
    +PublishManage(onClose)
  }

  class NormalModal {
    +boolean open
    +function onClose
    +string scroll
    +boolean overflowHidden
    +object okButtonProps
    +object cancelButtonProps
    +object classes
    +ReactNode title
  }

  ShareButton --> ShareTabs : renders in Popover
  ShareButton --> NormalModal : controls open state
  NormalModal --> PublishManage : renders as content

  ShareTabs --> PublishPanel : Panel option
  PublishPanel --> PublishLinkPreview : passes onOpenPublishManage
  PublishLinkPreview --> PublishManage : triggers via callback

  ShareButton ..> PublishLinkPreview : indirect via props
  ShareButton ..> PublishPanel : indirect via Tabs
  ShareTabs ..> PublishManage : indirect via callback chain
Loading

File-Level Changes

Change Details Files
Open the PublishManage modal from ShareButton instead of within PublishLinkPreview so that the share popover closes before namespace management opens.
  • Wrap ShareButton content in a fragment and add NormalModal with PublishManage, controlled by a new publishManageOpen state.
  • Pass an onOpenPublishManage callback from ShareButton into ShareTabs and down into PublishPanel/PublishLinkPreview.
  • Remove internal PublishManage NormalModal and related state from PublishLinkPreview, delegating open behavior to the new callback.
src/components/app/share/ShareButton.tsx
src/components/app/share/ShareTabs.tsx
src/components/app/share/PublishPanel.tsx
src/components/app/share/PublishLinkPreview.tsx
src/components/app/publish-manage/PublishManage.tsx
Introduce a Vite dev server middleware that redirects bare-namespace URLs to their default published page, mirroring deploy/server.ts.
  • Add a namespaceRedirectPlugin in vite.config.ts that inspects GET requests, skips known app routes and assets, and for single-path segments calls the backend /api/workspace/published/{namespace}.
  • On successful lookup, respond with a 302 redirect to /{namespace}/{publish_name}; otherwise fall through to Vite handling.
  • Ensure the plugin only runs in dev mode and guard isDev default when NODE_ENV is unset.
vite.config.ts
Improve deploy-time server logging, error handling, and fallback behavior for namespace/publish metadata lookups.
  • Make logger level configurable via LOG_LEVEL and stop writing logs to a file destination in deploy/server.ts.
  • Downgrade verbose metadata fetch logs from info to debug, and enrich error/warn messages with URL, namespace, and publishName context.
  • Track whether a redirect was attempted for a namespace-only request and log a warning when serving the fallback landing page without metadata.
deploy/server.ts
Extend Cypress tests and selectors to cover opening namespace publish settings and verify correct popover/modal behavior.
  • Add selectors for the publish settings button, publish manage modal, and publish manage panel data-testid.
  • Add an e2e test that signs in, publishes a page, opens namespace publish settings via the caret, asserts the share popover closes, the publish manage modal appears, shows the Namespace section, and closes via Escape.
cypress/e2e/page/publish-page.cy.ts
cypress/support/selectors.ts
Adjust environment/host configuration for local/official environment detection.
  • Change the OFFICIAL_HOSTNAMES set to treat bare localhost as official rather than localhost:8000, aligning with the base URL handling in dev tooling.
src/utils/subscription.ts

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey there - I've reviewed your changes - here's some feedback:

  • In vite.config.ts, consider filtering out falsy entries in the plugins array (e.g. plugins: [react(), isDev && namespaceRedirectPlugin(), ...].filter(Boolean)) instead of inserting undefined, as some tooling and typings expect plugins to always be valid objects.
  • The new Cypress test relies on several fixed cy.wait(...) calls (including a 5000ms wait); replacing these with waits on specific UI conditions (e.g. should('be.visible') or route aliases) will make the test less flaky and faster.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `vite.config.ts`, consider filtering out falsy entries in the `plugins` array (e.g. `plugins: [react(), isDev && namespaceRedirectPlugin(), ...].filter(Boolean)`) instead of inserting `undefined`, as some tooling and typings expect plugins to always be valid objects.
- The new Cypress test relies on several fixed `cy.wait(...)` calls (including a 5000ms wait); replacing these with waits on specific UI conditions (e.g. `should('be.visible')` or route aliases) will make the test less flaky and faster.

## Individual Comments

### Comment 1
<location> `vite.config.ts:33` </location>
<code_context>
        const pathname = url.pathname;

</code_context>

<issue_to_address>
**suggestion (code-quality):** Prefer object destructuring when accessing and using properties. ([`use-object-destructuring`](https://docs.sourcery.ai/Reference/Rules-and-In-Line-Suggestions/TypeScript/Default-Rules/use-object-destructuring))

```suggestion
        const {pathname} = url;
```

<br/><details><summary>Explanation</summary>Object destructuring can often remove an unnecessary temporary reference, as well as making your code more succinct.

From the [Airbnb Javascript Style Guide](https://airbnb.io/javascript/#destructuring--object)
</details>
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

}

const url = new URL(req.url, 'http://localhost');
const pathname = url.pathname;
Copy link

Choose a reason for hiding this comment

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

suggestion (code-quality): Prefer object destructuring when accessing and using properties. (use-object-destructuring)

Suggested change
const pathname = url.pathname;
const {pathname} = url;


ExplanationObject destructuring can often remove an unnecessary temporary reference, as well as making your code more succinct.

From the Airbnb Javascript Style Guide

@appflowy appflowy merged commit 95a596e into main Nov 26, 2025
12 checks passed
@appflowy appflowy deleted the fix_namespace_popover branch November 26, 2025 07:50
josue693 pushed a commit to josue693/AppFlowy-Web that referenced this pull request Dec 21, 2025
* fix: open publish manage modal after closing share popover

* chore: update log
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