diff --git a/apps/remix/app/root.tsx b/apps/remix/app/root.tsx index b7f65cce0..64ac118e6 100644 --- a/apps/remix/app/root.tsx +++ b/apps/remix/app/root.tsx @@ -30,6 +30,7 @@ import { RefreshOnFocus } from './components/general/refresh-on-focus'; import { PostHogPageview } from './providers/posthog'; import { langCookie } from './storage/lang-cookie.server'; import { themeSessionResolver } from './storage/theme-session.server'; +import { appMetaTags } from './utils/meta'; const { trackPageview } = Plausible({ domain: 'documenso.com', @@ -53,36 +54,9 @@ export const links: Route.LinksFunction = () => [ { rel: 'stylesheet', href: stylesheet }, ]; -// Todo: Meta data. -// export function generateMetadata() { -// return { -// title: { -// template: '%s - Documenso', -// default: 'Documenso', -// }, -// description: -// 'Join Documenso, the open signing infrastructure, and get a 10x better signing experience. Pricing starts at $30/mo. forever! Sign in now and enjoy a faster, smarter, and more beautiful document signing process. Integrates with your favorite tools, customizable, and expandable. Support our mission and become a part of our open-source community.', -// keywords: -// 'Documenso, open source, DocuSign alternative, document signing, open signing infrastructure, open-source community, fast signing, beautiful signing, smart templates', -// authors: { name: 'Documenso, Inc.' }, -// robots: 'index, follow', -// metadataBase: new URL(NEXT_PUBLIC_WEBAPP_URL() ?? 'http://localhost:3000'), -// openGraph: { -// title: 'Documenso - The Open Source DocuSign Alternative', -// description: -// 'Join Documenso, the open signing infrastructure, and get a 10x better signing experience. Pricing starts at $30/mo. forever! Sign in now and enjoy a faster, smarter, and more beautiful document signing process. Integrates with your favorite tools, customizable, and expandable. Support our mission and become a part of our open-source community.', -// type: 'website', -// images: ['/opengraph-image.jpg'], -// }, -// twitter: { -// site: '@documenso', -// card: 'summary_large_image', -// images: ['/opengraph-image.jpg'], -// description: -// 'Join Documenso, the open signing infrastructure, and get a 10x better signing experience. Pricing starts at $30/mo. forever! Sign in now and enjoy a faster, smarter, and more beautiful document signing process. Integrates with your favorite tools, customizable, and expandable. Support our mission and become a part of our open-source community.', -// }, -// }; -// } +export function meta() { + return appMetaTags(); +} export async function loader({ request }: Route.LoaderArgs) { const session = getOptionalLoaderSession(); diff --git a/apps/remix/app/routes/_authenticated+/documents+/_index.tsx b/apps/remix/app/routes/_authenticated+/documents+/_index.tsx index bf7a7f8c5..6c5ced573 100644 --- a/apps/remix/app/routes/_authenticated+/documents+/_index.tsx +++ b/apps/remix/app/routes/_authenticated+/documents+/_index.tsx @@ -25,9 +25,10 @@ import { DocumentsTable } from '~/components/tables/documents-table'; import { DocumentsTableEmptyState } from '~/components/tables/documents-table-empty-state'; import { DocumentsTableSenderFilter } from '~/components/tables/documents-table-sender-filter'; import { useOptionalCurrentTeam } from '~/providers/team'; +import { appMetaTags } from '~/utils/meta'; export function meta() { - return [{ title: 'Documents' }]; + return appMetaTags('Documents'); } const ZSearchParamsSchema = ZFindDocumentsInternalRequestSchema.pick({ diff --git a/apps/remix/app/routes/_authenticated+/settings+/_layout.tsx b/apps/remix/app/routes/_authenticated+/settings+/_layout.tsx index fc5e6a0bd..c1df197e9 100644 --- a/apps/remix/app/routes/_authenticated+/settings+/_layout.tsx +++ b/apps/remix/app/routes/_authenticated+/settings+/_layout.tsx @@ -3,6 +3,11 @@ import { Outlet } from 'react-router'; import { SettingsDesktopNav } from '~/components/general/settings-nav-desktop'; import { SettingsMobileNav } from '~/components/general/settings-nav-mobile'; +import { appMetaTags } from '~/utils/meta'; + +export function meta() { + return appMetaTags('Settings'); +} export default function SettingsLayout() { return ( diff --git a/apps/remix/app/routes/_authenticated+/settings+/profile.tsx b/apps/remix/app/routes/_authenticated+/settings+/profile.tsx index 7e60129c2..77909d147 100644 --- a/apps/remix/app/routes/_authenticated+/settings+/profile.tsx +++ b/apps/remix/app/routes/_authenticated+/settings+/profile.tsx @@ -5,9 +5,10 @@ import { AccountDeleteDialog } from '~/components/dialogs/account-delete-dialog' import { AvatarImageForm } from '~/components/forms/avatar-image'; import { ProfileForm } from '~/components/forms/profile'; import { SettingsHeader } from '~/components/general/settings-header'; +import { appMetaTags } from '~/utils/meta'; export function meta() { - return [{ title: 'Profile' }]; + return appMetaTags('Profile'); } export default function SettingsProfile() { diff --git a/apps/remix/app/routes/_authenticated+/settings+/security+/activity+/index.tsx b/apps/remix/app/routes/_authenticated+/settings+/security+/activity+/index.tsx index 3da3f448a..f39aea014 100644 --- a/apps/remix/app/routes/_authenticated+/settings+/security+/activity+/index.tsx +++ b/apps/remix/app/routes/_authenticated+/settings+/security+/activity+/index.tsx @@ -3,9 +3,10 @@ import { useLingui } from '@lingui/react'; import { SettingsHeader } from '~/components/general/settings-header'; import { SettingsSecurityActivityTable } from '~/components/tables/settings-security-activity-table'; +import { appMetaTags } from '~/utils/meta'; export function meta() { - return [{ title: 'Security activity' }]; + return appMetaTags('Security activity'); } export default function SettingsSecurityActivity() { diff --git a/apps/remix/app/routes/_authenticated+/settings+/security+/index.tsx b/apps/remix/app/routes/_authenticated+/settings+/security+/index.tsx index 66c680f47..8e505ed78 100644 --- a/apps/remix/app/routes/_authenticated+/settings+/security+/index.tsx +++ b/apps/remix/app/routes/_authenticated+/settings+/security+/index.tsx @@ -14,11 +14,12 @@ import { EnableAuthenticatorAppDialog } from '~/components/forms/2fa/enable-auth import { ViewRecoveryCodesDialog } from '~/components/forms/2fa/view-recovery-codes-dialog'; import { PasswordForm } from '~/components/forms/password'; import { SettingsHeader } from '~/components/general/settings-header'; +import { appMetaTags } from '~/utils/meta'; import type { Route } from './+types'; export function meta() { - return [{ title: 'Security' }]; + return appMetaTags('Security'); } export async function loader() { diff --git a/apps/remix/app/routes/_authenticated+/settings+/security+/passkeys+/index.tsx b/apps/remix/app/routes/_authenticated+/settings+/security+/passkeys+/index.tsx index 7cecf1522..53bb2165f 100644 --- a/apps/remix/app/routes/_authenticated+/settings+/security+/passkeys+/index.tsx +++ b/apps/remix/app/routes/_authenticated+/settings+/security+/passkeys+/index.tsx @@ -4,11 +4,10 @@ import { useLingui } from '@lingui/react'; import { PasskeyCreateDialog } from '~/components/dialogs/passkey-create-dialog'; import { SettingsHeader } from '~/components/general/settings-header'; import { SettingsSecurityPasskeyTable } from '~/components/tables/settings-security-passkey-table'; +import { appMetaTags } from '~/utils/meta'; -import type { Route } from './+types/index'; - -export function meta(_args: Route.MetaArgs) { - return [{ title: 'Manage passkeys' }]; +export function meta() { + return appMetaTags('Manage passkeys'); } export default function SettingsPasskeys() { diff --git a/apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings+/_layout.tsx b/apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings+/_layout.tsx index 2c06b75a3..7fb94db42 100644 --- a/apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings+/_layout.tsx +++ b/apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings+/_layout.tsx @@ -6,6 +6,11 @@ import { canExecuteTeamAction } from '@documenso/lib/utils/teams'; import { TeamSettingsNavDesktop } from '~/components/general/teams/team-settings-nav-desktop'; import { TeamSettingsNavMobile } from '~/components/general/teams/team-settings-nav-mobile'; +import { appMetaTags } from '~/utils/meta'; + +export function meta() { + return appMetaTags('Team Settings'); +} export function loader() { const { currentTeam: team } = getLoaderTeamSession(); diff --git a/apps/remix/app/routes/_authenticated+/templates+/_index.tsx b/apps/remix/app/routes/_authenticated+/templates+/_index.tsx index 71c0b5918..44908e7e3 100644 --- a/apps/remix/app/routes/_authenticated+/templates+/_index.tsx +++ b/apps/remix/app/routes/_authenticated+/templates+/_index.tsx @@ -10,9 +10,10 @@ import { Avatar, AvatarFallback, AvatarImage } from '@documenso/ui/primitives/av import { TemplateCreateDialog } from '~/components/dialogs/template-create-dialog'; import { TemplatesTable } from '~/components/tables/templates-table'; import { useOptionalCurrentTeam } from '~/providers/team'; +import { appMetaTags } from '~/utils/meta'; export function meta() { - return [{ title: 'Templates' }]; + return appMetaTags('Templates'); } export default function TemplatesPage() { diff --git a/apps/remix/app/routes/_unauthenticated+/check-email.tsx b/apps/remix/app/routes/_unauthenticated+/check-email.tsx index 868325d19..7eeb06eb0 100644 --- a/apps/remix/app/routes/_unauthenticated+/check-email.tsx +++ b/apps/remix/app/routes/_unauthenticated+/check-email.tsx @@ -3,8 +3,10 @@ import { Link } from 'react-router'; import { Button } from '@documenso/ui/primitives/button'; +import { appMetaTags } from '~/utils/meta'; + export function meta() { - return [{ title: 'Forgot password' }]; + return appMetaTags('Forgot password'); } export default function ForgotPasswordPage() { diff --git a/apps/remix/app/routes/_unauthenticated+/forgot-password.tsx b/apps/remix/app/routes/_unauthenticated+/forgot-password.tsx index 82aae2be0..bd661be72 100644 --- a/apps/remix/app/routes/_unauthenticated+/forgot-password.tsx +++ b/apps/remix/app/routes/_unauthenticated+/forgot-password.tsx @@ -2,9 +2,10 @@ import { Trans } from '@lingui/react/macro'; import { Link } from 'react-router'; import { ForgotPasswordForm } from '~/components/forms/forgot-password'; +import { appMetaTags } from '~/utils/meta'; export function meta() { - return [{ title: 'Forgot Password' }]; + return appMetaTags('Forgot Password'); } export default function ForgotPasswordPage() { diff --git a/apps/remix/app/routes/_unauthenticated+/reset-password.$token.tsx b/apps/remix/app/routes/_unauthenticated+/reset-password.$token.tsx index 531534a9d..1550658c0 100644 --- a/apps/remix/app/routes/_unauthenticated+/reset-password.$token.tsx +++ b/apps/remix/app/routes/_unauthenticated+/reset-password.$token.tsx @@ -4,11 +4,12 @@ import { Link, redirect } from 'react-router'; import { getResetTokenValidity } from '@documenso/lib/server-only/user/get-reset-token-validity'; import { ResetPasswordForm } from '~/components/forms/reset-password'; +import { appMetaTags } from '~/utils/meta'; import type { Route } from './+types/reset-password.$token'; export function meta() { - return [{ title: 'Reset Password' }]; + return appMetaTags('Reset Password'); } export async function loader({ params }: Route.LoaderArgs) { diff --git a/apps/remix/app/routes/_unauthenticated+/reset-password._index.tsx b/apps/remix/app/routes/_unauthenticated+/reset-password._index.tsx index cf334c8cd..f00874952 100644 --- a/apps/remix/app/routes/_unauthenticated+/reset-password._index.tsx +++ b/apps/remix/app/routes/_unauthenticated+/reset-password._index.tsx @@ -3,8 +3,10 @@ import { Link } from 'react-router'; import { Button } from '@documenso/ui/primitives/button'; +import { appMetaTags } from '~/utils/meta'; + export function meta() { - return [{ title: 'Reset Password' }]; + return appMetaTags('Reset Password'); } export default function ResetPasswordPage() { diff --git a/apps/remix/app/routes/_unauthenticated+/share.$slug.tsx b/apps/remix/app/routes/_unauthenticated+/share.$slug.tsx index e34f98f03..3126e9995 100644 --- a/apps/remix/app/routes/_unauthenticated+/share.$slug.tsx +++ b/apps/remix/app/routes/_unauthenticated+/share.$slug.tsx @@ -1,6 +1,6 @@ import { redirect } from 'react-router'; -import { NEXT_PUBLIC_MARKETING_URL } from '@documenso/lib/constants/app'; +import { NEXT_PUBLIC_MARKETING_URL, NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app'; import type { Route } from './+types/share.$slug'; @@ -10,20 +10,36 @@ export function meta({ params: { slug } }: Route.MetaArgs) { { title: 'Documenso - Share' }, { description: 'I just signed a document in style with Documenso!' }, { - openGraph: { - title: 'Documenso - Join the open source signing revolution', - description: 'I just signed with Documenso!', - type: 'website', - images: [`/share/${slug}/opengraph`], - }, + property: 'og:title', + title: 'Documenso - Join the open source signing revolution', }, { - twitter: { - site: '@documenso', - card: 'summary_large_image', - images: [`/share/${slug}/opengraph`], - description: 'I just signed with Documenso!', - }, + property: 'og:description', + description: 'I just signed with Documenso!', + }, + { + property: 'og:type', + type: 'website', + }, + { + property: 'og:images', + images: `${NEXT_PUBLIC_WEBAPP_URL()}/share/${slug}/opengraph`, + }, + { + name: 'twitter:site', + site: '@documenso', + }, + { + name: 'twitter:card', + card: 'summary_large_image', + }, + { + name: 'twitter:images', + images: `${NEXT_PUBLIC_WEBAPP_URL()}/share/${slug}/opengraph`, + }, + { + name: 'twitter:description', + description: 'I just signed with Documenso!', }, ]; } diff --git a/apps/remix/app/routes/_unauthenticated+/signin.tsx b/apps/remix/app/routes/_unauthenticated+/signin.tsx index 822403e73..af7aa38e5 100644 --- a/apps/remix/app/routes/_unauthenticated+/signin.tsx +++ b/apps/remix/app/routes/_unauthenticated+/signin.tsx @@ -10,11 +10,12 @@ import { import { env } from '@documenso/lib/utils/env'; import { SignInForm } from '~/components/forms/signin'; +import { appMetaTags } from '~/utils/meta'; import type { Route } from './+types/signin'; export function meta() { - return [{ title: 'Sign In' }]; + return appMetaTags('Sign In'); } export function loader() { diff --git a/apps/remix/app/routes/_unauthenticated+/signup.tsx b/apps/remix/app/routes/_unauthenticated+/signup.tsx index 4177a6871..0f7e8e953 100644 --- a/apps/remix/app/routes/_unauthenticated+/signup.tsx +++ b/apps/remix/app/routes/_unauthenticated+/signup.tsx @@ -4,11 +4,12 @@ import { IS_GOOGLE_SSO_ENABLED, IS_OIDC_SSO_ENABLED } from '@documenso/lib/const import { env } from '@documenso/lib/utils/env'; import { SignUpForm } from '~/components/forms/signup'; +import { appMetaTags } from '~/utils/meta'; import type { Route } from './+types/signup'; export function meta() { - return [{ title: 'Sign Up' }]; + return appMetaTags('Sign Up'); } export function loader() { diff --git a/apps/remix/app/routes/_unauthenticated+/verify-email._index.tsx b/apps/remix/app/routes/_unauthenticated+/verify-email._index.tsx index 4a79ff5df..d823d42ba 100644 --- a/apps/remix/app/routes/_unauthenticated+/verify-email._index.tsx +++ b/apps/remix/app/routes/_unauthenticated+/verify-email._index.tsx @@ -4,8 +4,10 @@ import { Link } from 'react-router'; import { Button } from '@documenso/ui/primitives/button'; +import { appMetaTags } from '~/utils/meta'; + export function meta() { - return [{ title: 'Verify Email' }]; + return appMetaTags('Verify Email'); } export default function EmailVerificationWithoutTokenPage() { diff --git a/apps/remix/app/utils/meta.ts b/apps/remix/app/utils/meta.ts new file mode 100644 index 000000000..1395831ac --- /dev/null +++ b/apps/remix/app/utils/meta.ts @@ -0,0 +1,61 @@ +import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app'; + +export const appMetaTags = (title?: string) => { + const description = + 'Join Documenso, the open signing infrastructure, and get a 10x better signing experience. Pricing starts at $30/mo. forever! Sign in now and enjoy a faster, smarter, and more beautiful document signing process. Integrates with your favorite tools, customizable, and expandable. Support our mission and become a part of our open-source community.'; + + return [ + { + title: title ? `${title} - Documenso` : 'Documenso', + }, + { + name: 'description', + content: description, + }, + { + name: 'keywords', + content: + 'Documenso, open source, DocuSign alternative, document signing, open signing infrastructure, open-source community, fast signing, beautiful signing, smart templates', + }, + { + name: 'author', + content: 'Documenso, Inc.', + }, + { + name: 'robots', + content: 'index, follow', + }, + { + property: 'og:title', + content: 'Documenso - The Open Source DocuSign Alternative', + }, + { + property: 'og:description', + content: description, + }, + { + property: 'og:image', + content: `${NEXT_PUBLIC_WEBAPP_URL()}/opengraph-image.jpg`, + }, + { + property: 'og:type', + content: 'website', + }, + { + name: 'twitter:card', + content: 'summary_large_image', + }, + { + name: 'twitter:site', + content: '@documenso', + }, + { + name: 'twitter:description', + content: description, + }, + { + name: 'twitter:image', + content: `${NEXT_PUBLIC_WEBAPP_URL()}/opengraph-image.jpg`, + }, + ]; +};