Skip to content
Open
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
12 changes: 0 additions & 12 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,15 +1,3 @@
# Tiptap Cloud configuration
# Info:
# You need a paid plan to use the Convert feature.

# Your Tiptap Cloud "App ID" - account-wide; the document import (Convert) uses it.
# See https://cloud.tiptap.dev/convert-settings (or any product settings page).
NEXT_PUBLIC_TIPTAP_APP_ID=

# Your Tiptap Convert "JWT Secret", see here https://cloud.tiptap.dev/convert-settings
# Used by the legacy @tiptap-pro/extension-import for .docx imports in the notebook.
TIPTAP_CONVERT_SECRET=

# Google OAuth 2.0 Client ID and Secret
# You can generate these from the Google Cloud Console
# https://console.cloud.google.com/apis/credentials
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import {
Pencil,
Sparkles,
} from 'lucide-react';
import { toast } from 'react-hot-toast';
import { Alert } from '@/components/ui/Alert';
import { Button } from '@/components/ui/Button';
import { Checkbox } from '@/components/ui/form/Checkbox';
Expand All @@ -33,9 +32,9 @@ import {
proposalDraftStepProgress,
} from '@/types/expertFinder';
import { useProposalDraft } from '@/hooks/useExpertFinder';
import { NoteService } from '@/services/note.service';
import { ExpertFormModal } from './ExpertFormModal';
import { ExpertSourceLinkIcon } from './ExpertSourceLinkIcon';
import { documentTarget, workspaceHref } from '@/components/AIMode/workspaceUrl';

interface ExpertResultCardProps {
expert: ExpertResult;
Expand All @@ -60,7 +59,6 @@ interface ProposalDraftSectionProps {
draft: ProposalDraft | null;
isStarting: boolean;
startError: string | null;
isOpeningNote: boolean;
onStart: () => void;
onOpenNote: () => void;
}
Expand All @@ -69,7 +67,6 @@ function ProposalDraftSection({
draft,
isStarting,
startError,
isOpeningNote,
onStart,
onOpenNote,
}: Readonly<ProposalDraftSectionProps>) {
Expand Down Expand Up @@ -126,13 +123,8 @@ function ProposalDraftSection({
size="sm"
className="w-full gap-2"
onClick={onOpenNote}
disabled={isOpeningNote}
>
{isOpeningNote ? (
<Loader2 className="h-4 w-4 shrink-0 animate-spin" aria-hidden />
) : (
<FileText className="h-4 w-4 shrink-0" aria-hidden />
)}
<FileText className="h-4 w-4 shrink-0" aria-hidden />
Open proposal
</Button>
);
Expand Down Expand Up @@ -169,7 +161,6 @@ export function ExpertResultCard({
}: ExpertResultCardProps) {
const router = useRouter();
const [editOpen, setEditOpen] = useState(false);
const [isOpeningNote, setIsOpeningNote] = useState(false);

// Refresh the search detail once a draft settles so selection-based flows
// (e.g. proposal invitation emails) see the latest draft state.
Expand All @@ -186,17 +177,9 @@ export function ExpertResultCard({
if (expert.searchExpertId != null) void startDraft(expert.searchExpertId);
}, [expert.searchExpertId, startDraft]);

const handleOpenNote = useCallback(async () => {
const handleOpenNote = useCallback(() => {
if (draft?.noteId == null) return;
setIsOpeningNote(true);
try {
const note = await NoteService.getNote(String(draft.noteId));
router.push(`/notebook/${note.organization.slug}/${draft.noteId}`);
} catch (e) {
toast.error(e instanceof Error ? e.message : 'Failed to open the proposal note');
} finally {
setIsOpeningNote(false);
}
router.push(workspaceHref(documentTarget(draft.noteId)));
}, [draft?.noteId, router]);
const name = empty(expert.name);
const canEditContact = expert.expertId != null && Boolean(onSuccess);
Expand Down Expand Up @@ -445,9 +428,8 @@ export function ExpertResultCard({
draft={draft}
isStarting={isStarting}
startError={startError}
isOpeningNote={isOpeningNote}
onStart={handleStartDraft}
onOpenNote={() => void handleOpenNote()}
onOpenNote={handleOpenNote}
/>
) : null}
{onGenerateEmail && (
Expand Down
2 changes: 2 additions & 0 deletions app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import localFont from 'next/font/local';

import './globals.css';
// The editor renders math with KaTeX wherever a document shows.
import 'katex/dist/katex.min.css';
import { Metadata } from 'next';
import { getServerSession } from 'next-auth';
import { authOptions } from './api/auth/[...nextauth]/auth.config';
Expand Down
62 changes: 52 additions & 10 deletions app/layouts/PublishMenu.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use client';

import { Plus } from 'lucide-react';
import { BaseMenu } from '@/components/ui/form/BaseMenu';
import { Plus, ScrollText } from 'lucide-react';
import { BaseMenu, BaseMenuItem } from '@/components/ui/form/BaseMenu';
import { useAuthenticatedAction } from '@/contexts/AuthModalContext';
import { SwipeableDrawer } from '@/components/ui/SwipeableDrawer';
import {
Expand All @@ -11,9 +11,19 @@ import {
type FundingDraftOption,
} from '@/components/Funding/fundingDraftOptions';
import { useFundingDrafting } from '@/components/Funding/useFundingDrafting';
import { useChangelogDrafting } from '@/components/changelog/useChangelogDrafting';
import { useUser } from '@/contexts/UserContext';
import { useScreenSize } from '@/hooks/useScreenSize';
import { useState } from 'react';

/** Moderators also publish product updates; the entry drafts in the workspace too. */
const CHANGELOG_OPTION = {
id: 'changelog',
title: 'ChangeLog',
description: 'Publish a ResearchHub product update',
icon: <ScrollText className="h-[18px] w-[18px] text-gray-700" />,
};

interface PublishMenuProps {
forceMinimize?: boolean;
/** Runs once an item is picked, for a host that should then get out of the way. */
Expand All @@ -23,14 +33,17 @@ interface PublishMenuProps {
/**
* The sidebar's Publish button: a Request for Proposal or a Proposal, either
* of which leads straight to a new draft in the workspace for that side of
* the money.
* the money, and for moderators a ChangeLog entry.
*/
export const PublishMenu: React.FC<PublishMenuProps> = ({
forceMinimize = false,
onItemSelected,
}) => {
const { executeAuthenticatedAction } = useAuthenticatedAction();
const { startNew } = useFundingDrafting();
const { startChangelog } = useChangelogDrafting();
const { user } = useUser();
const isModerator = !!user?.isModerator;
const { smAndDown } = useScreenSize();
const [isMobileDrawerOpen, setIsMobileDrawerOpen] = useState(false);

Expand All @@ -44,6 +57,16 @@ export const PublishMenu: React.FC<PublishMenuProps> = ({
onItemSelected?.();
};

const handleChangelogClick = () => {
executeAuthenticatedAction(() => {
void startChangelog();
});
if (smAndDown) {
setIsMobileDrawerOpen(false);
}
onItemSelected?.();
};

// Regular trigger for standard mode
const standardTrigger = (
<button
Expand Down Expand Up @@ -79,27 +102,46 @@ export const PublishMenu: React.FC<PublishMenuProps> = ({
</button>
);

const menuContent = <FundingDraftMenuItems onSelect={handleMenuItemClick} />;
const menuContent = (
<div className="space-y-1">
<FundingDraftMenuItems onSelect={handleMenuItemClick} />
{isModerator && (
<BaseMenuItem
onClick={handleChangelogClick}
className="group w-full cursor-pointer rounded-lg px-2 py-2 transition-colors duration-150 hover:bg-gray-100 focus:bg-gray-100"
>
<FundingDraftOptionContent option={CHANGELOG_OPTION} />
</BaseMenuItem>
)}
</div>
);

// Mobile drawer content
const mobileItems = [
...FUNDING_DRAFT_OPTIONS.map((item) => ({
option: item,
onPick: () => handleMenuItemClick(item),
})),
...(isModerator ? [{ option: CHANGELOG_OPTION, onPick: handleChangelogClick }] : []),
];
const mobileDrawerContent = (
<div className="space-y-2 pb-[max(1rem,env(safe-area-inset-bottom))]">
{FUNDING_DRAFT_OPTIONS.map((item) => (
{mobileItems.map(({ option, onPick }) => (
<div
key={item.id}
onClick={() => handleMenuItemClick(item)}
key={option.id}
onClick={onPick}
onKeyDown={(e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
handleMenuItemClick(item);
onPick();
}
}}
className="group w-full px-3 py-3 cursor-pointer rounded-xl transition-colors duration-150 hover:bg-gray-100 active:bg-gray-100"
role="button"
tabIndex={0}
aria-label={`${item.title}: ${item.description}`}
aria-label={`${option.title}: ${option.description}`}
>
<FundingDraftOptionContent option={item} />
<FundingDraftOptionContent option={option} />
</div>
))}
</div>
Expand Down
1 change: 0 additions & 1 deletion app/layouts/topbar/pageRoutes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ export const ROOT_NAVIGATION_PATHS = new Set([
...HOME_TAB_PATHS,
'/peer-review',
'/journal',
'/notebook',
'/lists',
'/settings',
'/endowment',
Expand Down
135 changes: 0 additions & 135 deletions app/note/join/[inviteKey]/page.tsx

This file was deleted.

Loading
Loading