Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion cypress/e2e/database2/filter-date.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,15 @@ const selectDateByDay = (day: number): void => {
return text === String(day) && !el.classList.contains('day-outside');
})
.first()
.click({ force: true });
.then(($el) => {
// In react-day-picker single mode, clicking an already-selected day deselects it.
// When a date filter is first added, it defaults to today's date. If the desired
// day matches today, clicking would toggle it off instead of keeping it selected.
// Skip the click if the day is already selected.
if ($el.attr('aria-selected') !== 'true') {
cy.wrap($el).click({ force: true });
}
});
waitForReactUpdate(500);
};

Expand Down
42 changes: 17 additions & 25 deletions src/components/app/ViewModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { useTranslation } from 'react-i18next';
import { toast } from 'sonner';

import { APP_EVENTS } from '@/application/constants';
import { UIVariant, ViewComponentProps, ViewLayout, ViewMetaProps, YDoc, YDocWithMeta } from '@/application/types';
import { UIVariant, View, ViewComponentProps, ViewLayout, ViewMetaProps, YDoc, YDocWithMeta } from '@/application/types';
import { getFirstChildView } from '@/application/view-utils';
import { ReactComponent as ArrowDownIcon } from '@/assets/icons/alt_arrow_down.svg';
import { ReactComponent as CloseIcon } from '@/assets/icons/close.svg';
Expand Down Expand Up @@ -46,15 +46,6 @@ const Transition = React.forwardRef(function Transition(
return <Zoom ref={ref} {...props} />;
});

/**
* Minimal view metadata used as fallback when view is not yet in outline
*/
interface FallbackViewMeta {
view_id: string;
layout: ViewLayout;
name: string;
}

function ViewModal({ viewId, open, onClose }: { viewId?: string; open: boolean; onClose: () => void }) {
const workspaceId = useCurrentWorkspaceId();
const { t } = useTranslation();
Expand Down Expand Up @@ -87,8 +78,10 @@ function ViewModal({ viewId, open, onClose }: { viewId?: string; open: boolean;
const [notFound, setNotFound] = useState(false);
const [syncBound, setSyncBound] = useState(false);

// Fallback view metadata fetched from server (used when view not in outline yet)
const [fallbackMeta, setFallbackMeta] = useState<FallbackViewMeta | null>(null);
// Fallback view metadata fetched from server (used when view not in outline yet).
// Stores the full View (including children/extra) so getFirstChildView can
// resolve database containers to their first child view.
const [fallbackMeta, setFallbackMeta] = useState<View | null>(null);

// Get view from outline
const outlineView = useMemo(() => {
Expand Down Expand Up @@ -135,11 +128,7 @@ function ViewModal({ viewId, open, onClose }: { viewId?: string; open: boolean;
ViewService.get(workspaceId, effectiveViewId)
.then((fetchedView) => {
if (!cancelled && fetchedView) {
setFallbackMeta({
view_id: fetchedView.view_id,
layout: fetchedView.layout,
name: fetchedView.name,
});
setFallbackMeta(fetchedView);
}
})
.catch((e) => {
Expand Down Expand Up @@ -169,14 +158,17 @@ function ViewModal({ viewId, open, onClose }: { viewId?: string; open: boolean;
[loadView]
);

// Wait for metadata (outline or fallback) before loading the doc.
// This ensures database containers resolve to their first child view,
// and the correct layout is known for the page-view API call.
const resolvedView = effectiveOutlineView || fallbackMeta;

useEffect(() => {
if (open && effectiveViewId) {
if (open && effectiveViewId && resolvedView) {
void loadPageDoc(effectiveViewId);
}
}, [open, effectiveViewId, loadPageDoc]);
}, [open, effectiveViewId, loadPageDoc, resolvedView]);

// Use outline view if available, otherwise use fallback
const resolvedView = effectiveOutlineView || fallbackMeta;
const layout = resolvedView?.layout ?? ViewLayout.Document;

// Build viewMeta for the View component
Expand All @@ -197,15 +189,15 @@ function ViewModal({ viewId, open, onClose }: { viewId?: string; open: boolean;
};
}

// Fallback with minimal properties
// Fallback: resolvedView is a full View from the server
return {
name: resolvedView.name,
icon: undefined,
cover: undefined,
icon: resolvedView.icon || undefined,
cover: resolvedView.extra?.cover || undefined,
layout: resolvedView.layout,
visibleViewIds: [],
viewId: resolvedView.view_id,
extra: undefined,
extra: resolvedView.extra,
workspaceId,
};
}, [resolvedView, effectiveOutlineView, workspaceId]);
Expand Down
2 changes: 1 addition & 1 deletion src/components/editor/components/leaf/Leaf.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ export function Leaf({ attributes, children, leaf, text }: RenderLeafProps) {
);
}

if (leaf.href) {
if (leaf.href && text.text?.trim()) {
newChildren = (
<Href text={text} leaf={leaf} textColor={style['color']}>
{newChildren}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export function MentionLeaf({ mention, text, children }: { mention: Mention; tex
top: isCursorBefore ? 0 : 'auto',
bottom: isCursorBefore ? 'auto' : 0,
}}
className={'absolute bottom-0 right-0 overflow-hidden !text-transparent'}
className={'absolute bottom-0 right-0 overflow-hidden !text-transparent pointer-events-none'}
>
{children}
</span>
Expand Down
31 changes: 16 additions & 15 deletions src/components/editor/components/leaf/mention/MentionPage.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useCallback, useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Element, Text } from 'slate';
import { ReactEditor, useReadOnly, useSlate } from 'slate-react';
import { ReactEditor, useSlate } from 'slate-react';
import smoothScrollIntoViewIfNeeded from 'smooth-scroll-into-view-if-needed';

import { APP_EVENTS } from '@/application/constants';
Expand All @@ -19,7 +19,6 @@ import { useEditorContext } from '@/components/editor/EditorContext';
import './style.css';

function MentionPage({
text,
pageId,
blockId,
type,
Expand All @@ -35,7 +34,7 @@ function MentionPage({
const currentViewId = context.viewId;
const eventEmitter = context.eventEmitter;

const { navigateToView, loadViewMeta, loadView, openPageModal } = context;
const { navigateToView, loadViewMeta, loadView } = context;
const [noAccess, setNoAccess] = useState(false);
const [meta, setMeta] = useState<View | null>(null);
const [content, setContent] = useState<string>('');
Expand Down Expand Up @@ -157,8 +156,6 @@ function MentionPage({
);
}, [blockId, currentViewId, icon, meta?.layout, pageId, type]);

const readOnly = useReadOnly() || editor.isElementReadOnly(text as unknown as Element);

const handleScrollToBlock = useCallback(async () => {
if (blockId) {
const entry = findSlateEntryByBlockId(editor as YjsEditor, blockId);
Expand All @@ -184,22 +181,26 @@ function MentionPage({
<span
onClick={(e) => {
e.stopPropagation();
if (readOnly || meta?.layout === ViewLayout.AIChat) {
void navigateToView?.(pageId, blockId);
} else {
if (noAccess) return;
if (pageId === currentViewId) {
void handleScrollToBlock();
return;
}
if (noAccess) return;

openPageModal?.(pageId);
// Same-page block reference: scroll to the target block
if (pageId === currentViewId) {
void handleScrollToBlock();
return;
}

// Navigate directly to the target view (matches desktop behavior,
// which always opens mentions in a new tab regardless of layout type).
// Deferred so Slate's event cycle completes before the route change
// unmounts the editor.
setTimeout(() => {
void navigateToView?.(pageId, blockId);
}, 0);
}}
style={{
cursor: noAccess ? 'default' : undefined,
}}
className={`mention-inline cursor-pointer pr-1 underline`}
className={`mention-inline cursor-pointer pr-1 underline select-none`}
contentEditable={false}
data-mention-id={pageId}
>
Expand Down
Loading