mirror of
https://github.com/documenso/documenso.git
synced 2025-11-13 00:03:33 +10:00
feat: migrate nextjs to rr7
This commit is contained in:
@ -1,4 +1,4 @@
|
||||
import type { DocumentData } from '@documenso/prisma/client';
|
||||
import type { DocumentData } from '@prisma/client';
|
||||
|
||||
import { getFile } from '../universal/upload/get-file';
|
||||
import { downloadFile } from './download-file';
|
||||
|
||||
@ -1,13 +1,9 @@
|
||||
import { posthog } from 'posthog-js';
|
||||
|
||||
import { useFeatureFlags } from '@documenso/lib/client-only/providers/feature-flag';
|
||||
import {
|
||||
FEATURE_FLAG_GLOBAL_SESSION_RECORDING,
|
||||
extractPostHogConfig,
|
||||
} from '@documenso/lib/constants/feature-flags';
|
||||
import { extractPostHogConfig } from '@documenso/lib/constants/feature-flags';
|
||||
|
||||
export function useAnalytics() {
|
||||
const featureFlags = useFeatureFlags();
|
||||
// const featureFlags = useFeatureFlags();
|
||||
const isPostHogEnabled = extractPostHogConfig();
|
||||
|
||||
/**
|
||||
@ -30,27 +26,29 @@ export function useAnalytics() {
|
||||
* @param eventFlag The event to check against feature flags to determine whether tracking is enabled.
|
||||
*/
|
||||
const startSessionRecording = (eventFlag?: string) => {
|
||||
const isSessionRecordingEnabled = featureFlags.getFlag(FEATURE_FLAG_GLOBAL_SESSION_RECORDING);
|
||||
const isSessionRecordingEnabledForEvent = Boolean(eventFlag && featureFlags.getFlag(eventFlag));
|
||||
return;
|
||||
// const isSessionRecordingEnabled = featureFlags.getFlag(FEATURE_FLAG_GLOBAL_SESSION_RECORDING);
|
||||
// const isSessionRecordingEnabledForEvent = Boolean(eventFlag && featureFlags.getFlag(eventFlag));
|
||||
|
||||
if (!isPostHogEnabled || !isSessionRecordingEnabled || !isSessionRecordingEnabledForEvent) {
|
||||
return;
|
||||
}
|
||||
// if (!isPostHogEnabled || !isSessionRecordingEnabled || !isSessionRecordingEnabledForEvent) {
|
||||
// return;
|
||||
// }
|
||||
|
||||
posthog.startSessionRecording();
|
||||
// posthog.startSessionRecording();
|
||||
};
|
||||
|
||||
/**
|
||||
* Stop the current session recording.
|
||||
*/
|
||||
const stopSessionRecording = () => {
|
||||
const isSessionRecordingEnabled = featureFlags.getFlag(FEATURE_FLAG_GLOBAL_SESSION_RECORDING);
|
||||
return;
|
||||
// const isSessionRecordingEnabled = featureFlags.getFlag(FEATURE_FLAG_GLOBAL_SESSION_RECORDING);
|
||||
|
||||
if (!isPostHogEnabled || !isSessionRecordingEnabled) {
|
||||
return;
|
||||
}
|
||||
// if (!isPostHogEnabled || !isSessionRecordingEnabled) {
|
||||
// return;
|
||||
// }
|
||||
|
||||
posthog.stopSessionRecording();
|
||||
// posthog.stopSessionRecording();
|
||||
};
|
||||
|
||||
return {
|
||||
|
||||
@ -1,5 +1,3 @@
|
||||
'use client';
|
||||
|
||||
import { useCallback } from 'react';
|
||||
|
||||
import { getBoundingClientRect } from '@documenso/lib/client-only/get-bounding-client-rect';
|
||||
|
||||
@ -1,25 +0,0 @@
|
||||
import { useMemo } from 'react';
|
||||
|
||||
import type { CombinedStylesKey } from '../../../ui/primitives/document-flow/add-fields';
|
||||
import { combinedStyles } from '../../../ui/primitives/document-flow/field-item';
|
||||
|
||||
const defaultFieldItemStyles = {
|
||||
borderClass: 'border-field-card-border',
|
||||
activeBorderClass: 'border-field-card-border/80',
|
||||
initialsBGClass: 'text-field-card-foreground/50 bg-slate-900/10',
|
||||
fieldBackground: 'bg-field-card-background',
|
||||
};
|
||||
|
||||
export const useFieldItemStyles = (color: CombinedStylesKey | null) => {
|
||||
return useMemo(() => {
|
||||
if (!color) return defaultFieldItemStyles;
|
||||
|
||||
const selectedColorVariant = combinedStyles[color];
|
||||
return {
|
||||
activeBorderClass: selectedColorVariant?.borderActive,
|
||||
borderClass: selectedColorVariant?.border,
|
||||
initialsBGClass: selectedColorVariant?.initialsBG,
|
||||
fieldBackground: selectedColorVariant?.fieldBackground,
|
||||
};
|
||||
}, [color]);
|
||||
};
|
||||
@ -1,8 +1,9 @@
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
|
||||
import type { Field } from '@prisma/client';
|
||||
|
||||
import { getBoundingClientRect } from '@documenso/lib/client-only/get-bounding-client-rect';
|
||||
import { PDF_VIEWER_PAGE_SELECTOR } from '@documenso/lib/constants/pdf-viewer';
|
||||
import type { Field } from '@documenso/prisma/client';
|
||||
|
||||
export const useFieldPageCoords = (field: Field) => {
|
||||
const [coords, setCoords] = useState({
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import { usePathname, useRouter, useSearchParams } from 'next/navigation';
|
||||
import { useLocation, useNavigate, useSearchParams } from 'react-router';
|
||||
|
||||
export const useUpdateSearchParams = () => {
|
||||
const router = useRouter();
|
||||
const pathname = usePathname();
|
||||
const searchParams = useSearchParams();
|
||||
const navigate = useNavigate();
|
||||
const { pathname } = useLocation();
|
||||
const [searchParams] = useSearchParams();
|
||||
|
||||
return (params: Record<string, string | number | boolean | null | undefined>) => {
|
||||
const nextSearchParams = new URLSearchParams(searchParams?.toString() ?? '');
|
||||
@ -16,6 +16,6 @@ export const useUpdateSearchParams = () => {
|
||||
}
|
||||
});
|
||||
|
||||
router.push(`${pathname}?${nextSearchParams.toString()}`);
|
||||
void navigate(`${pathname}?${nextSearchParams.toString()}`);
|
||||
};
|
||||
};
|
||||
|
||||
@ -1,95 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import { createContext, useCallback, useContext, useEffect, useState } from 'react';
|
||||
|
||||
import {
|
||||
FEATURE_FLAG_POLL_INTERVAL,
|
||||
LOCAL_FEATURE_FLAGS,
|
||||
isFeatureFlagEnabled,
|
||||
} from '@documenso/lib/constants/feature-flags';
|
||||
import { getAllFlags } from '@documenso/lib/universal/get-feature-flag';
|
||||
|
||||
import { TFeatureFlagValue } from './feature-flag.types';
|
||||
|
||||
export type FeatureFlagContextValue = {
|
||||
getFlag: (_key: string) => TFeatureFlagValue;
|
||||
};
|
||||
|
||||
export const FeatureFlagContext = createContext<FeatureFlagContextValue | null>(null);
|
||||
|
||||
export const useFeatureFlags = () => {
|
||||
const context = useContext(FeatureFlagContext);
|
||||
|
||||
if (!context) {
|
||||
throw new Error('useFeatureFlags must be used within a FeatureFlagProvider');
|
||||
}
|
||||
|
||||
return context;
|
||||
};
|
||||
|
||||
export function FeatureFlagProvider({
|
||||
children,
|
||||
initialFlags,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
initialFlags: Record<string, TFeatureFlagValue>;
|
||||
}) {
|
||||
const [flags, setFlags] = useState(initialFlags);
|
||||
|
||||
const getFlag = useCallback(
|
||||
(flag: string) => {
|
||||
if (!isFeatureFlagEnabled()) {
|
||||
return LOCAL_FEATURE_FLAGS[flag] ?? true;
|
||||
}
|
||||
|
||||
return flags[flag] ?? false;
|
||||
},
|
||||
[flags],
|
||||
);
|
||||
|
||||
/**
|
||||
* Refresh the flags every `FEATURE_FLAG_POLL_INTERVAL` amount of time if the window is focused.
|
||||
*/
|
||||
useEffect(() => {
|
||||
if (!isFeatureFlagEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const interval = setInterval(() => {
|
||||
if (document.hasFocus()) {
|
||||
void getAllFlags().then((newFlags) => setFlags(newFlags));
|
||||
}
|
||||
}, FEATURE_FLAG_POLL_INTERVAL);
|
||||
|
||||
return () => {
|
||||
clearInterval(interval);
|
||||
};
|
||||
}, []);
|
||||
|
||||
/**
|
||||
* Refresh the flags when the window is focused.
|
||||
*/
|
||||
useEffect(() => {
|
||||
if (!isFeatureFlagEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const onFocus = () => void getAllFlags().then((newFlags) => setFlags(newFlags));
|
||||
|
||||
window.addEventListener('focus', onFocus);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('focus', onFocus);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<FeatureFlagContext.Provider
|
||||
value={{
|
||||
getFlag,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</FeatureFlagContext.Provider>
|
||||
);
|
||||
}
|
||||
@ -1,10 +0,0 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
export const ZFeatureFlagValueSchema = z.union([
|
||||
z.boolean(),
|
||||
z.string(),
|
||||
z.number(),
|
||||
z.undefined(),
|
||||
]);
|
||||
|
||||
export type TFeatureFlagValue = z.infer<typeof ZFeatureFlagValueSchema>;
|
||||
@ -1,17 +1,12 @@
|
||||
import 'server-only';
|
||||
|
||||
import { cookies, headers } from 'next/headers';
|
||||
|
||||
import type { I18n, Messages } from '@lingui/core';
|
||||
import { setupI18n } from '@lingui/core';
|
||||
import { setI18n } from '@lingui/react/server';
|
||||
|
||||
import {
|
||||
APP_I18N_OPTIONS,
|
||||
SUPPORTED_LANGUAGE_CODES,
|
||||
isValidLanguageCode,
|
||||
} from '../../constants/i18n';
|
||||
import { extractLocaleData } from '../../utils/i18n';
|
||||
import { env } from '../../utils/env';
|
||||
import { remember } from '../../utils/remember';
|
||||
|
||||
type SupportedLanguages = (typeof SUPPORTED_LANGUAGE_CODES)[number];
|
||||
@ -19,7 +14,7 @@ type SupportedLanguages = (typeof SUPPORTED_LANGUAGE_CODES)[number];
|
||||
export async function loadCatalog(lang: SupportedLanguages): Promise<{
|
||||
[k: string]: Messages;
|
||||
}> {
|
||||
const extension = process.env.NODE_ENV === 'development' ? 'po' : 'js';
|
||||
const extension = env('NODE_ENV') === 'development' ? 'po' : 'mjs';
|
||||
|
||||
const { messages } = await import(`../../translations/${lang}/web.${extension}`);
|
||||
|
||||
@ -71,29 +66,3 @@ export const getI18nInstance = async (lang?: SupportedLanguages | (string & {}))
|
||||
|
||||
return instances[lang] ?? instances[APP_I18N_OPTIONS.sourceLang];
|
||||
};
|
||||
|
||||
/**
|
||||
* This needs to be run in all layouts and page server components that require i18n.
|
||||
*
|
||||
* https://lingui.dev/tutorials/react-rsc#pages-layouts-and-lingui
|
||||
*/
|
||||
export const setupI18nSSR = async () => {
|
||||
const { lang, locales } = extractLocaleData({
|
||||
cookies: cookies(),
|
||||
headers: headers(),
|
||||
});
|
||||
|
||||
// Get and set a ready-made i18n instance for the given language.
|
||||
const i18n = await getI18nInstance(lang);
|
||||
|
||||
// Reactivate the i18n instance with the locale for date and number formatting.
|
||||
i18n.activate(lang, locales);
|
||||
|
||||
setI18n(i18n);
|
||||
|
||||
return {
|
||||
lang,
|
||||
locales,
|
||||
i18n,
|
||||
};
|
||||
};
|
||||
@ -1,5 +1,3 @@
|
||||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
|
||||
import { type Messages, setupI18n } from '@lingui/core';
|
||||
|
||||
45
packages/lib/client-only/providers/session.tsx
Normal file
45
packages/lib/client-only/providers/session.tsx
Normal file
@ -0,0 +1,45 @@
|
||||
import { createContext, useContext } from 'react';
|
||||
import React from 'react';
|
||||
|
||||
import type { SessionUser } from '@documenso/auth/server/lib/session/session';
|
||||
import type { Session } from '@documenso/prisma/client';
|
||||
|
||||
import type { TGetTeamByUrlResponse } from '../../server-only/team/get-team';
|
||||
import type { TGetTeamsResponse } from '../../server-only/team/get-teams';
|
||||
|
||||
export type AppSession = {
|
||||
session: Session;
|
||||
user: SessionUser;
|
||||
currentTeam: TGetTeamByUrlResponse | null;
|
||||
teams: TGetTeamsResponse;
|
||||
};
|
||||
|
||||
interface SessionProviderProps {
|
||||
children: React.ReactNode;
|
||||
session: AppSession | null;
|
||||
}
|
||||
|
||||
const SessionContext = createContext<AppSession | null>(null);
|
||||
|
||||
export const useSession = () => {
|
||||
const context = useContext(SessionContext);
|
||||
|
||||
if (!context) {
|
||||
throw new Error('useSession must be used within a SessionProvider');
|
||||
}
|
||||
|
||||
return context;
|
||||
};
|
||||
|
||||
export const useOptionalSession = () => {
|
||||
return (
|
||||
useContext(SessionContext) || {
|
||||
user: null,
|
||||
session: null,
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
export const SessionProvider = ({ children, session }: SessionProviderProps) => {
|
||||
return <SessionContext.Provider value={session}>{children}</SessionContext.Provider>;
|
||||
};
|
||||
@ -1,11 +1,11 @@
|
||||
import type { Recipient } from '@documenso/prisma/client';
|
||||
import type { Recipient } from '@prisma/client';
|
||||
import {
|
||||
DocumentDistributionMethod,
|
||||
ReadStatus,
|
||||
RecipientRole,
|
||||
SendStatus,
|
||||
SigningStatus,
|
||||
} from '@documenso/prisma/client';
|
||||
} from '@prisma/client';
|
||||
|
||||
export enum RecipientStatusType {
|
||||
COMPLETED = 'completed',
|
||||
|
||||
Reference in New Issue
Block a user