feat: use next-intl (#1026)
* feat: use next-intl * feat: set next-intl localePrefix to never * chore: add next-intl * chore: fix lock file * feat: use [locale] * fix: the custom rewrite conflict with next-intl rewrite in middleware * fix: getTranslations function name * fix: remove comma in translation key * fix: next-intl dev error * feat: update tran text * feat: transaction for about page * feat: i18n for server components * feat: i18n for client components * feat: i18n for client components and provider * feat: i18n for language switch * feat: remove i18next * feat: custom intl error, ignore MISSING_MESSAGE error * feat: use useLocale * fix: unstable_setRequestLocale for middleware error
This commit is contained in:
parent
b311d6a975
commit
91aa185d1c
|
|
@ -1,7 +1,7 @@
|
|||
[
|
||||
{
|
||||
"name": "xLog",
|
||||
"description": "Let's talk about xLog, whether to praise or criticize it.",
|
||||
"description": "Let's talk about xLog, whether to praise or criticize it",
|
||||
"includeTags": ["xLog"],
|
||||
"includeKeywords": ["xLog"],
|
||||
"excludeKeywords": ["同步更新至 xLog"]
|
||||
|
|
@ -29,13 +29,13 @@
|
|||
},
|
||||
{
|
||||
"name": "AI",
|
||||
"description": "The world is undergoing changes, and we are at the center of it.",
|
||||
"description": "The world is undergoing changes, and we are at the center of it",
|
||||
"includeTags": ["AI"],
|
||||
"includeKeywords": ["GPT", "Midjourney", "AIGC", " AI ", "OpenAI"]
|
||||
},
|
||||
{
|
||||
"name": "Journal",
|
||||
"description": "Discover new perspectives and insights in others' weekly, monthly, or yearly journal.",
|
||||
"description": "Discover new perspectives and insights in others' weekly, monthly, or yearly journal",
|
||||
"includeTags": ["Journal", "日记"],
|
||||
"includeKeywords": [
|
||||
"Review",
|
||||
|
|
@ -49,7 +49,7 @@
|
|||
},
|
||||
{
|
||||
"name": "Fiction",
|
||||
"description": "Experience a world of imagination through these fiction stories.",
|
||||
"description": "Experience a world of imagination through these fiction stories",
|
||||
"includeTags": ["Fiction", "小说"],
|
||||
"notes": [
|
||||
"55405-3",
|
||||
|
|
|
|||
183
next.config.js
183
next.config.js
|
|
@ -5,105 +5,110 @@ const withBundleAnalyzer = require("@next/bundle-analyzer")({
|
|||
})
|
||||
const execSync = require("child_process").execSync
|
||||
|
||||
const withNextIntl = require("next-intl/plugin")()
|
||||
|
||||
const lastCommitCommand = "git rev-parse HEAD"
|
||||
|
||||
module.exports = withBundleAnalyzer({
|
||||
output: "standalone",
|
||||
env: {
|
||||
APP_DESCRIPTION: pkg.description,
|
||||
},
|
||||
experimental: {
|
||||
scrollRestoration: true,
|
||||
serverComponentsExternalPackages: ["rehype-react"],
|
||||
},
|
||||
productionBrowserSourceMaps: true,
|
||||
module.exports = withBundleAnalyzer(
|
||||
withNextIntl({
|
||||
output: "standalone",
|
||||
env: {
|
||||
APP_DESCRIPTION: pkg.description,
|
||||
},
|
||||
experimental: {
|
||||
scrollRestoration: true,
|
||||
serverComponentsExternalPackages: ["rehype-react"],
|
||||
},
|
||||
productionBrowserSourceMaps: true,
|
||||
|
||||
webpack(config) {
|
||||
config.resolve.fallback = { fs: false } // polyfill node-id3
|
||||
webpack(config) {
|
||||
config.resolve.fallback = { fs: false } // polyfill node-id3
|
||||
|
||||
// https://github.com/WalletConnect/walletconnect-monorepo/blob/7716e164281c2f531145d682c3658f761fa0a823/providers/universal-provider/src/utils/deepLinks.ts#L39
|
||||
// @walletconnect/universal-provider imports react-native conditionally.
|
||||
// Since this is a NextJS app, simply mark it as external to avoid the webpack bundling warning.
|
||||
config.externals.push("react-native")
|
||||
// https://github.com/WalletConnect/walletconnect-monorepo/blob/7716e164281c2f531145d682c3658f761fa0a823/providers/universal-provider/src/utils/deepLinks.ts#L39
|
||||
// @walletconnect/universal-provider imports react-native conditionally.
|
||||
// Since this is a NextJS app, simply mark it as external to avoid the webpack bundling warning.
|
||||
config.externals.push("react-native")
|
||||
|
||||
// https://github.com/WalletConnect/walletconnect-monorepo/issues/1908#issuecomment-1487801131
|
||||
config.externals.push("pino-pretty", "lokijs", "encoding")
|
||||
// https://github.com/WalletConnect/walletconnect-monorepo/issues/1908#issuecomment-1487801131
|
||||
config.externals.push("pino-pretty", "lokijs", "encoding")
|
||||
|
||||
// https://github.com/WalletConnect/walletconnect-utils/blob/b7d7dc003c25dd33ef74c2fac483140f71a51d86/jsonrpc/http-connection/src/http.ts#L2
|
||||
// `@walletconnect/jsonrpc-http-connection` imports `cross-fetch` to support fetch in Node.js. It's unnecessary for Next.JS app.
|
||||
config.resolve.alias["cross-fetch"] = require.resolve(
|
||||
"next/dist/build/polyfills/fetch/index.js",
|
||||
)
|
||||
// https://github.com/WalletConnect/walletconnect-utils/blob/b7d7dc003c25dd33ef74c2fac483140f71a51d86/jsonrpc/http-connection/src/http.ts#L2
|
||||
// `@walletconnect/jsonrpc-http-connection` imports `cross-fetch` to support fetch in Node.js. It's unnecessary for Next.JS app.
|
||||
config.resolve.alias["cross-fetch"] = require.resolve(
|
||||
"next/dist/build/polyfills/fetch/index.js",
|
||||
)
|
||||
|
||||
// https://github.com/kkomelin/isomorphic-dompurify/issues/54
|
||||
// Fix isomorphic-dompurify in app router
|
||||
config.externals = [...config.externals, "jsdom", "sharp"]
|
||||
// https://github.com/kkomelin/isomorphic-dompurify/issues/54
|
||||
// Fix isomorphic-dompurify in app router
|
||||
config.externals = [...config.externals, "jsdom", "sharp"]
|
||||
|
||||
return config
|
||||
},
|
||||
return config
|
||||
},
|
||||
|
||||
images: {
|
||||
dangerouslyAllowSVG: true,
|
||||
contentSecurityPolicy:
|
||||
"default-src 'self'; script-src 'none'; sandbox; style-src 'unsafe-inline';",
|
||||
remotePatterns: [
|
||||
{ hostname: "**" },
|
||||
{ protocol: "https", hostname: "pbs.twimg.com" },
|
||||
{ protocol: "https", hostname: "abs.twimg.com" },
|
||||
],
|
||||
},
|
||||
images: {
|
||||
dangerouslyAllowSVG: true,
|
||||
contentSecurityPolicy:
|
||||
"default-src 'self'; script-src 'none'; sandbox; style-src 'unsafe-inline';",
|
||||
remotePatterns: [
|
||||
{ hostname: "**" },
|
||||
{ protocol: "https", hostname: "pbs.twimg.com" },
|
||||
{ protocol: "https", hostname: "abs.twimg.com" },
|
||||
],
|
||||
},
|
||||
|
||||
async generateBuildId() {
|
||||
return execSync(lastCommitCommand).toString().trim()
|
||||
},
|
||||
async generateBuildId() {
|
||||
return execSync(lastCommitCommand).toString().trim()
|
||||
},
|
||||
|
||||
serverRuntimeConfig: {
|
||||
ENV_REDIS_URL: process.env.REDIS_URL,
|
||||
ENV_REDIS_EXPIRE: process.env.REDIS_EXPIRE,
|
||||
ENV_REDIS_REFRESH: process.env.REDIS_REFRESH,
|
||||
ENV_SIMPLEHASH_API_KEY: process.env.SIMPLEHASH_API_KEY,
|
||||
ENV_OPENAI_API_KEY: process.env.OPENAI_API_KEY,
|
||||
ENV_ANONYMOUS_ACCOUNT_PRIVATEKEY: process.env.ANONYMOUS_ACCOUNT_PRIVATEKEY,
|
||||
ENV_PORTFOLIO_GITHUB_TOKEN: process.env.PORTFOLIO_GITHUB_TOKEN,
|
||||
},
|
||||
serverRuntimeConfig: {
|
||||
ENV_REDIS_URL: process.env.REDIS_URL,
|
||||
ENV_REDIS_EXPIRE: process.env.REDIS_EXPIRE,
|
||||
ENV_REDIS_REFRESH: process.env.REDIS_REFRESH,
|
||||
ENV_SIMPLEHASH_API_KEY: process.env.SIMPLEHASH_API_KEY,
|
||||
ENV_OPENAI_API_KEY: process.env.OPENAI_API_KEY,
|
||||
ENV_ANONYMOUS_ACCOUNT_PRIVATEKEY:
|
||||
process.env.ANONYMOUS_ACCOUNT_PRIVATEKEY,
|
||||
ENV_PORTFOLIO_GITHUB_TOKEN: process.env.PORTFOLIO_GITHUB_TOKEN,
|
||||
},
|
||||
|
||||
async headers() {
|
||||
return [
|
||||
{
|
||||
source: "/.well-known/apple-app-site-association",
|
||||
headers: [
|
||||
{
|
||||
key: "Content-Type",
|
||||
value: "application/json",
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
},
|
||||
staticPageGenerationTimeout: 3600,
|
||||
async headers() {
|
||||
return [
|
||||
{
|
||||
source: "/.well-known/apple-app-site-association",
|
||||
headers: [
|
||||
{
|
||||
key: "Content-Type",
|
||||
value: "application/json",
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
},
|
||||
staticPageGenerationTimeout: 3600,
|
||||
|
||||
async redirects() {
|
||||
return [
|
||||
{
|
||||
source: "/sitemap.txt",
|
||||
destination: "/sitemap.xml",
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: "/sitemap.xml.gz",
|
||||
destination: "/sitemap.xml",
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: "/sitemap_index.xml",
|
||||
destination: "/sitemap.xml",
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: "/sitemaps.xml",
|
||||
destination: "/sitemap.xml",
|
||||
permanent: true,
|
||||
},
|
||||
]
|
||||
},
|
||||
})
|
||||
async redirects() {
|
||||
return [
|
||||
{
|
||||
source: "/sitemap.txt",
|
||||
destination: "/sitemap.xml",
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: "/sitemap.xml.gz",
|
||||
destination: "/sitemap.xml",
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: "/sitemap_index.xml",
|
||||
destination: "/sitemap.xml",
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: "/sitemaps.xml",
|
||||
destination: "/sitemap.xml",
|
||||
permanent: true,
|
||||
},
|
||||
]
|
||||
},
|
||||
}),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -81,8 +81,6 @@
|
|||
"fast-deep-equal": "3.1.3",
|
||||
"hast-util-from-html": "2.0.1",
|
||||
"hast-util-to-html": "9.0.0",
|
||||
"i18next": "23.7.6",
|
||||
"i18next-resources-to-backend": "1.2.0",
|
||||
"immer": "10.0.3",
|
||||
"ioredis": "5.3.2",
|
||||
"isomorphic-dompurify": "1.9.0",
|
||||
|
|
@ -98,6 +96,7 @@
|
|||
"mermaid": "10.6.1",
|
||||
"nanoid": "5.0.3",
|
||||
"next": "14.0.1",
|
||||
"next-intl": "^3.1.2",
|
||||
"next-themes": "0.2.1",
|
||||
"node-id3": "0.2.6",
|
||||
"open-graph-scraper": "^6.3.2",
|
||||
|
|
@ -114,7 +113,6 @@
|
|||
"react-dom": "18.2.0",
|
||||
"react-hook-form": "7.48.2",
|
||||
"react-hot-toast": "2.4.1",
|
||||
"react-i18next": "13.5.0",
|
||||
"react-parallax-tilt": "1.7.175",
|
||||
"react-popper": "2.3.0",
|
||||
"react-sortablejs": "6.1.4",
|
||||
|
|
|
|||
2277
pnpm-lock.yaml
2277
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
|
|
@ -1,15 +1,15 @@
|
|||
import { Metadata } from "next"
|
||||
import { getTranslations } from "next-intl/server"
|
||||
|
||||
import { dehydrate, Hydrate } from "@tanstack/react-query"
|
||||
|
||||
import { HomeFeed } from "~/components/home/HomeFeed"
|
||||
import ParticipateButton from "~/components/home/ParticipateButton"
|
||||
import { APP_NAME } from "~/lib/env"
|
||||
import { getTranslation } from "~/lib/i18n"
|
||||
import getQueryClient from "~/lib/query-client"
|
||||
import { prefetchGetFeed } from "~/queries/home.server"
|
||||
|
||||
import topics from "../../../../../../data/topics.json"
|
||||
import topics from "../../../../../../../data/topics.json"
|
||||
|
||||
export function generateMetadata({
|
||||
params,
|
||||
|
|
@ -31,7 +31,7 @@ export default async function Topic({
|
|||
topic: string
|
||||
}
|
||||
}) {
|
||||
const { t } = await getTranslation("index")
|
||||
const t = await getTranslations()
|
||||
params.topic = decodeURIComponent(params.topic)
|
||||
const info = topics.find((t) => t.name === params.topic)
|
||||
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
import { getTranslations } from "next-intl/server"
|
||||
import Link from "next/link"
|
||||
import { memo } from "react"
|
||||
|
||||
|
|
@ -10,10 +11,9 @@ import { Button } from "~/components/ui/Button"
|
|||
import { Image } from "~/components/ui/Image"
|
||||
import { CSB_SCAN, GITHUB_LINK } from "~/lib/env"
|
||||
import { getSiteLink } from "~/lib/helpers"
|
||||
import { getTranslation, Trans } from "~/lib/i18n"
|
||||
|
||||
async function Home() {
|
||||
const { t, i18n } = await getTranslation("index")
|
||||
const t = await getTranslations()
|
||||
|
||||
const EntranceButton = memo(function Button() {
|
||||
return (
|
||||
|
|
@ -118,32 +118,33 @@ async function Home() {
|
|||
className={`text-feature text-feature-${feature.title.toLocaleLowerCase()} inline-block sm:mr-1`}
|
||||
>
|
||||
{t(feature.title)}
|
||||
{t(".")}
|
||||
{t("comma")}
|
||||
</span>
|
||||
))}
|
||||
</h2>
|
||||
<h3 className="mt-3 sm:mt-5 text-zinc-800 text-2xl sm:text-4xl font-light">
|
||||
<Trans i18n={i18n} i18nKey="description" ns="index">
|
||||
An{" "}
|
||||
<a
|
||||
target="_blank"
|
||||
rel="nofollow noreferrer"
|
||||
className="underline decoration-2 text-green-400 font-medium"
|
||||
href={GITHUB_LINK}
|
||||
>
|
||||
open-source
|
||||
</a>{" "}
|
||||
creative community written on the{" "}
|
||||
<a
|
||||
target="_blank"
|
||||
rel="nofollow noreferrer"
|
||||
className="underline decoration-2 text-yellow-400 font-medium"
|
||||
href={CSB_SCAN}
|
||||
>
|
||||
blockchain
|
||||
</a>
|
||||
.
|
||||
</Trans>
|
||||
{t.rich("description", {
|
||||
opensourceLink: (chunks) => (
|
||||
<a
|
||||
target="_blank"
|
||||
rel="nofollow noreferrer"
|
||||
className="underline decoration-2 text-green-400 font-medium"
|
||||
href={GITHUB_LINK}
|
||||
>
|
||||
{chunks}
|
||||
</a>
|
||||
),
|
||||
blockchainLink: (chunks) => (
|
||||
<a
|
||||
target="_blank"
|
||||
rel="nofollow noreferrer"
|
||||
className="underline decoration-2 text-yellow-400 font-medium"
|
||||
href={CSB_SCAN}
|
||||
>
|
||||
{chunks}
|
||||
</a>
|
||||
),
|
||||
})}
|
||||
</h3>
|
||||
<div className="my-4 sm:my-12 flex items-center justify-center sm:flex-row flex-col">
|
||||
<EntranceButton />
|
||||
|
|
@ -193,23 +194,18 @@ async function Home() {
|
|||
{t(`features.${feature.title}.subtitle`)}
|
||||
</h4>
|
||||
<p className="mt-6 text-zinc-500">
|
||||
<Trans
|
||||
i18n={i18n}
|
||||
i18nKey={`features.${feature.title}.description`}
|
||||
components={{
|
||||
aown: (
|
||||
<a
|
||||
target="_blank"
|
||||
rel="nofollow noreferrer"
|
||||
className="underline"
|
||||
href="https://github.com/Crossbell-Box/Crossbell-Contracts/wiki"
|
||||
>
|
||||
.
|
||||
</a>
|
||||
),
|
||||
}}
|
||||
ns="index"
|
||||
/>
|
||||
{t.rich(`features.${feature.title}.description`, {
|
||||
aown: (chunks) => (
|
||||
<a
|
||||
target="_blank"
|
||||
rel="nofollow noreferrer"
|
||||
className="underline"
|
||||
href="https://github.com/Crossbell-Box/Crossbell-Contracts/wiki"
|
||||
>
|
||||
{chunks}
|
||||
</a>
|
||||
),
|
||||
})}
|
||||
</p>
|
||||
</div>
|
||||
<ul className="pt-10 grid grid-cols-1 sm:grid-cols-3 gap-8">
|
||||
|
|
@ -237,38 +233,35 @@ async function Home() {
|
|||
</span>
|
||||
</p>
|
||||
<p className="text-base font-light mt-3 mb-4 leading-normal px-5 text-left">
|
||||
<Trans
|
||||
i18n={i18n}
|
||||
i18nKey={`${item.title} text`}
|
||||
components={{
|
||||
strong: <strong className="font-bold" />,
|
||||
axfm: (
|
||||
<a
|
||||
target="_blank"
|
||||
rel="nofollow noreferrer"
|
||||
className="underline"
|
||||
href={`${getSiteLink({
|
||||
subdomain: "xlog",
|
||||
})}/xfm`}
|
||||
>
|
||||
.
|
||||
</a>
|
||||
),
|
||||
aincentive: (
|
||||
<a
|
||||
target="_blank"
|
||||
rel="nofollow noreferrer"
|
||||
className="underline"
|
||||
href={`${getSiteLink({
|
||||
subdomain: "xlog",
|
||||
})}/creator-incentive-plan`}
|
||||
>
|
||||
.
|
||||
</a>
|
||||
),
|
||||
}}
|
||||
ns="index"
|
||||
/>
|
||||
{t.rich(`${item.title} text`, {
|
||||
strong: (chunks) => (
|
||||
<strong className="font-bold">{chunks}</strong>
|
||||
),
|
||||
axfm: (chunks) => (
|
||||
<a
|
||||
target="_blank"
|
||||
rel="nofollow noreferrer"
|
||||
className="underline"
|
||||
href={`${getSiteLink({
|
||||
subdomain: "xlog",
|
||||
})}/xfm`}
|
||||
>
|
||||
{chunks}
|
||||
</a>
|
||||
),
|
||||
aincentive: (chunks) => (
|
||||
<a
|
||||
target="_blank"
|
||||
rel="nofollow noreferrer"
|
||||
className="underline"
|
||||
href={`${getSiteLink({
|
||||
subdomain: "xlog",
|
||||
})}/creator-incentive-plan`}
|
||||
>
|
||||
{chunks}
|
||||
</a>
|
||||
),
|
||||
})}
|
||||
</p>
|
||||
</li>
|
||||
))}
|
||||
|
|
@ -319,7 +312,7 @@ async function Home() {
|
|||
<div className="my-10 text-zinc-700">
|
||||
<p className="text-xl">
|
||||
{t(
|
||||
"xLog's open design allows it to integrate with many other open protocols and applications without friction.",
|
||||
"xLog's open design allows it to integrate with many other open protocols and applications without friction",
|
||||
)}
|
||||
</p>
|
||||
<ul className="mt-14 grid grid-cols-3 sm:grid-cols-5 gap-y-14 gap-x-2">
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
import { getLocale, getTranslations } from "next-intl/server"
|
||||
import { notFound } from "next/navigation"
|
||||
|
||||
import { dehydrate, Hydrate } from "@tanstack/react-query"
|
||||
|
|
@ -8,7 +9,6 @@ import PostModal from "~/components/home/PostModal"
|
|||
import { PostFooter } from "~/components/site/PostFooter"
|
||||
import PostMeta from "~/components/site/PostMeta"
|
||||
import { SiteHeader } from "~/components/site/SiteHeader"
|
||||
import { getTranslation } from "~/lib/i18n"
|
||||
import { toCid } from "~/lib/ipfs-parser"
|
||||
import { isOnlyContent } from "~/lib/is-only-content"
|
||||
import getQueryClient from "~/lib/query-client"
|
||||
|
|
@ -46,13 +46,13 @@ export default async function SiteModal({
|
|||
|
||||
const dehydratedState = dehydrate(queryClient)
|
||||
|
||||
const { i18n } = await getTranslation()
|
||||
const { t } = await getTranslation("common")
|
||||
const locale = await getLocale()
|
||||
const t = await getTranslations()
|
||||
let summary: string | undefined
|
||||
if (!page.metadata.content.disableAISummary) {
|
||||
summary = await getSummary({
|
||||
cid: toCid(page.metadata?.uri || ""),
|
||||
lang: i18n.resolvedLanguage,
|
||||
lang: locale,
|
||||
})
|
||||
}
|
||||
const onlyContent = isOnlyContent()
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
"use client"
|
||||
|
||||
import { useTranslations } from "next-intl"
|
||||
import { useParams } from "next/navigation"
|
||||
import { useEffect } from "react"
|
||||
import { Virtuoso } from "react-virtuoso"
|
||||
|
|
@ -8,13 +9,12 @@ import { CommentItem } from "~/components/common/CommentItem"
|
|||
import { DashboardMain } from "~/components/dashboard/DashboardMain"
|
||||
import { UniLink } from "~/components/ui/UniLink"
|
||||
import { getSiteLink } from "~/lib/helpers"
|
||||
import { Trans, useTranslation } from "~/lib/i18n/client"
|
||||
import { useGetCommentsBySite, useGetSite } from "~/queries/site"
|
||||
|
||||
export default function CommentsPage() {
|
||||
const params = useParams()
|
||||
const subdomain = params?.subdomain as string
|
||||
const { t, i18n } = useTranslation("dashboard")
|
||||
const t = useTranslations()
|
||||
|
||||
const site = useGetSite(subdomain)
|
||||
|
||||
|
|
@ -46,7 +46,7 @@ export default function CommentsPage() {
|
|||
<div className="text-sm text-zinc-500 leading-relaxed">
|
||||
<p>
|
||||
{t(
|
||||
"You can subscribe to comments through an RSS reader to receive timely reminders.",
|
||||
"You can subscribe to comments through an RSS reader to receive timely reminders",
|
||||
)}
|
||||
</p>
|
||||
<p>
|
||||
|
|
@ -95,28 +95,18 @@ export default function CommentsPage() {
|
|||
<div key={comment.transactionHash} className="mt-6">
|
||||
<div>
|
||||
{name}{" "}
|
||||
<Trans
|
||||
i18n={i18n}
|
||||
i18nKey="comment on your"
|
||||
values={{
|
||||
type: t(type || "", {
|
||||
ns: "common",
|
||||
}),
|
||||
toTitle,
|
||||
}}
|
||||
defaults="commented on your {{type}} <tolink>{{toTitle}}</tolink>"
|
||||
components={{
|
||||
tolink: (
|
||||
<UniLink
|
||||
href={`/api/redirection?characterId=${comment.characterId}¬eId=${comment.noteId}`}
|
||||
target="_blank"
|
||||
>
|
||||
.
|
||||
</UniLink>
|
||||
),
|
||||
}}
|
||||
ns="dashboard"
|
||||
/>
|
||||
{t.rich("comment on your", {
|
||||
tolink: (chunks) => (
|
||||
<UniLink
|
||||
href={`/api/redirection?characterId=${comment.characterId}¬eId=${comment.noteId}`}
|
||||
target="_blank"
|
||||
>
|
||||
{chunks}
|
||||
</UniLink>
|
||||
),
|
||||
type: t(type),
|
||||
toTitle,
|
||||
})}
|
||||
:
|
||||
</div>
|
||||
<CommentItem
|
||||
|
|
@ -137,7 +127,7 @@ export default function CommentsPage() {
|
|||
}
|
||||
|
||||
const Loader = () => {
|
||||
const { t } = useTranslation("common")
|
||||
const t = useTranslations()
|
||||
return (
|
||||
<div
|
||||
className="relative mt-4 w-full text-sm text-center py-4"
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
"use client"
|
||||
|
||||
import { nanoid } from "nanoid"
|
||||
import { useTranslations } from "next-intl"
|
||||
import { useParams, useRouter, useSearchParams } from "next/navigation"
|
||||
import { memo, useCallback, useEffect, useState } from "react"
|
||||
import toast from "react-hot-toast"
|
||||
|
|
@ -24,7 +25,6 @@ import { showConfetti } from "~/lib/confetti"
|
|||
import { crossbell2Editor } from "~/lib/editor-converter"
|
||||
import { CSB_SCAN } from "~/lib/env"
|
||||
import { getTwitterShareUrl } from "~/lib/helpers"
|
||||
import { useTranslation } from "~/lib/i18n/client"
|
||||
import { getPageVisibility } from "~/lib/page-helpers"
|
||||
import { delStorage, setStorage } from "~/lib/storage"
|
||||
import { EditorValues, PageVisibilityEnum } from "~/lib/types"
|
||||
|
|
@ -40,7 +40,7 @@ import { useGetSite } from "~/queries/site"
|
|||
export default function PortfolioEditor() {
|
||||
const router = useRouter()
|
||||
const queryClient = useQueryClient()
|
||||
const { t } = useTranslation("dashboard")
|
||||
const t = useTranslations()
|
||||
const params = useParams()
|
||||
const subdomain = params?.subdomain as string
|
||||
const searchParams = useSearchParams()
|
||||
|
|
@ -285,8 +285,8 @@ export default function PortfolioEditor() {
|
|||
visibility === PageVisibilityEnum.Draft
|
||||
? `text-zinc-300`
|
||||
: visibility === PageVisibilityEnum.Modified
|
||||
? "text-orange-600"
|
||||
: "text-green-600",
|
||||
? "text-orange-600"
|
||||
: "text-green-600",
|
||||
)}
|
||||
>
|
||||
{t(visibility as string)}
|
||||
|
|
@ -302,7 +302,7 @@ export default function PortfolioEditor() {
|
|||
|
||||
const EditorExtraProperties = memo(
|
||||
({ updateValue }: { updateValue: (val: EditorValues) => void }) => {
|
||||
const { t } = useTranslation("dashboard")
|
||||
const t = useTranslations()
|
||||
|
||||
return (
|
||||
<div className="w-full space-y-5">
|
||||
|
|
@ -316,7 +316,7 @@ const EditorExtraProperties = memo(
|
|||
<EditorPublishAt
|
||||
updateValue={updateValue}
|
||||
prompt={t(
|
||||
"This portfolio will be accessible from this time. Leave blank to use the current time.",
|
||||
"This portfolio will be accessible from this time Leave blank to use the current time",
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
"use client"
|
||||
|
||||
import { nanoid } from "nanoid"
|
||||
import { useTranslations } from "next-intl"
|
||||
import { useParams, useRouter, useSearchParams } from "next/navigation"
|
||||
import { memo, useCallback, useEffect, useState } from "react"
|
||||
import toast from "react-hot-toast"
|
||||
|
|
@ -32,7 +33,6 @@ import { getDefaultSlug } from "~/lib/default-slug"
|
|||
import { crossbell2Editor } from "~/lib/editor-converter"
|
||||
import { CSB_SCAN } from "~/lib/env"
|
||||
import { getSiteLink, getTwitterShareUrl } from "~/lib/helpers"
|
||||
import { useTranslation } from "~/lib/i18n/client"
|
||||
import { getPageVisibility } from "~/lib/page-helpers"
|
||||
import { delStorage, setStorage } from "~/lib/storage"
|
||||
import {
|
||||
|
|
@ -54,7 +54,7 @@ import { useGetSite } from "~/queries/site"
|
|||
export default function PostEditor() {
|
||||
const router = useRouter()
|
||||
const queryClient = useQueryClient()
|
||||
const { t } = useTranslation("dashboard")
|
||||
const t = useTranslations()
|
||||
const params = useParams()
|
||||
const subdomain = params?.subdomain as string
|
||||
const searchParams = useSearchParams()
|
||||
|
|
@ -156,9 +156,7 @@ export default function PostEditor() {
|
|||
// Replace all invalid chars
|
||||
val.slug = val.slug.replace(/[^\w\-]/g, "-")
|
||||
toast.error(
|
||||
t(
|
||||
"Slug can only contain letters, numbers, hyphens, and underscores.",
|
||||
),
|
||||
t("Slug can only contain letters, numbers, hyphens, and underscores"),
|
||||
)
|
||||
}
|
||||
const newValues = { ...values, ...val }
|
||||
|
|
@ -429,8 +427,8 @@ export default function PostEditor() {
|
|||
visibility === PageVisibilityEnum.Draft
|
||||
? `text-zinc-300`
|
||||
: visibility === PageVisibilityEnum.Modified
|
||||
? "text-orange-600"
|
||||
: "text-green-600",
|
||||
? "text-orange-600"
|
||||
: "text-green-600",
|
||||
)}
|
||||
>
|
||||
{t(visibility as string)}
|
||||
|
|
@ -491,7 +489,7 @@ export default function PostEditor() {
|
|||
})
|
||||
}
|
||||
className="h-12 ml-1 inline-flex items-center border-none text-3xl font-bold w-full focus:outline-none bg-white"
|
||||
placeholder={t("Title goes here...") || ""}
|
||||
placeholder={t("Title goes here") || ""}
|
||||
/>
|
||||
</div>
|
||||
<div className="mt-5 flex-1 min-h-0">
|
||||
|
|
@ -527,7 +525,7 @@ const EditorExtraProperties = memo(
|
|||
characterId?: number
|
||||
siteLink?: string
|
||||
}) => {
|
||||
const { t } = useTranslation("dashboard")
|
||||
const t = useTranslations()
|
||||
|
||||
const { present } = useModalStack()
|
||||
const openAdvancedOptions = () => {
|
||||
|
|
@ -539,7 +537,7 @@ const EditorExtraProperties = memo(
|
|||
<EditorPublishAt
|
||||
updateValue={updateValue}
|
||||
prompt={t(
|
||||
`This ${type} will be accessible from this time. Leave blank to use the current time.`,
|
||||
`This ${type} will be accessible from this time Leave blank to use the current time`,
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
"use client"
|
||||
|
||||
import { nanoid } from "nanoid"
|
||||
import { useTranslations } from "next-intl"
|
||||
import { useParams, useRouter, useSearchParams } from "next/navigation"
|
||||
import { memo, useCallback, useEffect, useState } from "react"
|
||||
import toast from "react-hot-toast"
|
||||
|
|
@ -22,7 +23,6 @@ import { showConfetti } from "~/lib/confetti"
|
|||
import { crossbell2Editor } from "~/lib/editor-converter"
|
||||
import { CSB_SCAN } from "~/lib/env"
|
||||
import { getTwitterShareUrl } from "~/lib/helpers"
|
||||
import { useTranslation } from "~/lib/i18n/client"
|
||||
import { getPageVisibility } from "~/lib/page-helpers"
|
||||
import { delStorage, setStorage } from "~/lib/storage"
|
||||
import { EditorValues, PageVisibilityEnum } from "~/lib/types"
|
||||
|
|
@ -38,7 +38,7 @@ import { useGetSite } from "~/queries/site"
|
|||
export default function ShortEditor() {
|
||||
const router = useRouter()
|
||||
const queryClient = useQueryClient()
|
||||
const { t } = useTranslation("dashboard")
|
||||
const t = useTranslations()
|
||||
const params = useParams()
|
||||
const subdomain = params?.subdomain as string
|
||||
const searchParams = useSearchParams()
|
||||
|
|
@ -132,7 +132,7 @@ export default function ShortEditor() {
|
|||
|
||||
const savePage = async () => {
|
||||
if (!values.images?.length) {
|
||||
toast.error(t("Please upload at least one image."))
|
||||
toast.error(t("Please upload at least one image"))
|
||||
return
|
||||
}
|
||||
const baseValues = {
|
||||
|
|
@ -287,8 +287,8 @@ export default function ShortEditor() {
|
|||
visibility === PageVisibilityEnum.Draft
|
||||
? `text-zinc-300`
|
||||
: visibility === PageVisibilityEnum.Modified
|
||||
? "text-orange-600"
|
||||
: "text-green-600",
|
||||
? "text-orange-600"
|
||||
: "text-green-600",
|
||||
)}
|
||||
>
|
||||
{t(visibility as string)}
|
||||
|
|
@ -304,7 +304,7 @@ export default function ShortEditor() {
|
|||
|
||||
const EditorExtraProperties = memo(
|
||||
({ updateValue }: { updateValue: (val: EditorValues) => void }) => {
|
||||
const { t } = useTranslation("dashboard")
|
||||
const t = useTranslations()
|
||||
|
||||
return (
|
||||
<div className="w-full space-y-5">
|
||||
|
|
@ -314,7 +314,7 @@ const EditorExtraProperties = memo(
|
|||
<EditorPublishAt
|
||||
updateValue={updateValue}
|
||||
prompt={t(
|
||||
"This short will be accessible from this time. Leave blank to use the current time.",
|
||||
"This short will be accessible from this time Leave blank to use the current time",
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
"use client"
|
||||
|
||||
import { useTranslations } from "next-intl"
|
||||
import { useEffect, useState } from "react"
|
||||
|
||||
import { CharacterFloatCard } from "~/components/common/CharacterFloatCard"
|
||||
|
|
@ -9,7 +10,6 @@ import { Image } from "~/components/ui/Image"
|
|||
import { UniLink } from "~/components/ui/UniLink"
|
||||
import { useDate } from "~/hooks/useDate"
|
||||
import { getSiteLink } from "~/lib/helpers"
|
||||
import { useTranslation } from "~/lib/i18n/client"
|
||||
import { getStorage, setStorage } from "~/lib/storage"
|
||||
import { cn } from "~/lib/utils"
|
||||
import { useGetPagesBySite } from "~/queries/page"
|
||||
|
|
@ -39,7 +39,7 @@ function SiteAvatar({ siteId }: { siteId: string }) {
|
|||
|
||||
export default function EventsPage() {
|
||||
const date = useDate()
|
||||
const { t } = useTranslation("dashboard")
|
||||
const t = useTranslations()
|
||||
const pages = useGetPagesBySite({
|
||||
type: "post",
|
||||
characterId: 50153,
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
"use client"
|
||||
|
||||
import type { NoteMetadata } from "crossbell"
|
||||
import { useTranslations } from "next-intl"
|
||||
import { useParams } from "next/navigation"
|
||||
import { useEffect, useState } from "react"
|
||||
import { useForm } from "react-hook-form"
|
||||
|
|
@ -10,7 +11,6 @@ import { DashboardMain } from "~/components/dashboard/DashboardMain"
|
|||
import { ImportPreview } from "~/components/dashboard/ImportPreview"
|
||||
import { Button } from "~/components/ui/Button"
|
||||
import { Input } from "~/components/ui/Input"
|
||||
import { useTranslation } from "~/lib/i18n/client"
|
||||
import { readFiles } from "~/lib/read-files"
|
||||
import { usePostNotes } from "~/queries/page"
|
||||
import { useGetSite } from "~/queries/site"
|
||||
|
|
@ -19,7 +19,7 @@ export default function ImportMarkdownPage() {
|
|||
const params = useParams()
|
||||
const subdomain = params?.subdomain as string
|
||||
const site = useGetSite(subdomain)
|
||||
const { t } = useTranslation("dashboard")
|
||||
const t = useTranslations()
|
||||
const form = useForm({
|
||||
defaultValues: {
|
||||
type: "post",
|
||||
|
|
@ -81,7 +81,7 @@ export default function ImportMarkdownPage() {
|
|||
accept=".md"
|
||||
multiple={true}
|
||||
help={t(
|
||||
"Please select .md files, multiple files are supported, and front matter is supported.",
|
||||
"Please select md files, multiple files are supported, and front matter is supported",
|
||||
)}
|
||||
{...form.register("files", {})}
|
||||
/>
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
"use client"
|
||||
|
||||
import type { NoteMetadata } from "crossbell"
|
||||
import { useTranslations } from "next-intl"
|
||||
import { useParams } from "next/navigation"
|
||||
import { useEffect } from "react"
|
||||
import { useForm } from "react-hook-form"
|
||||
|
|
@ -9,7 +10,6 @@ import { toast } from "react-hot-toast"
|
|||
import { DashboardMain } from "~/components/dashboard/DashboardMain"
|
||||
import { ImportPreview } from "~/components/dashboard/ImportPreview"
|
||||
import { Button } from "~/components/ui/Button"
|
||||
import { useTranslation } from "~/lib/i18n/client"
|
||||
import { useCheckMirror, useGetMirrorXyz, usePostNotes } from "~/queries/page"
|
||||
import { useGetSite } from "~/queries/site"
|
||||
|
||||
|
|
@ -17,7 +17,7 @@ export default function ImportMirrorPage() {
|
|||
const params = useParams()
|
||||
const subdomain = params?.subdomain as string
|
||||
const site = useGetSite(subdomain)
|
||||
const { t } = useTranslation("dashboard")
|
||||
const t = useTranslations()
|
||||
const form = useForm({
|
||||
defaultValues: {
|
||||
type: "post",
|
||||
|
|
@ -62,13 +62,13 @@ export default function ImportMirrorPage() {
|
|||
}, [postNotes.isSuccess, form])
|
||||
|
||||
return (
|
||||
<DashboardMain title="Import from Mirror.xyz">
|
||||
<DashboardMain title="Import from Mirrorxyz">
|
||||
{checkMirror?.data ? (
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div className="min-w-[270px] max-w-screen-lg flex flex-col space-y-4">
|
||||
<div>
|
||||
<div className="form-label">
|
||||
{t("Preview your Mirror.xyz entries")}
|
||||
{t("Preview your Mirrorxyz entries")}
|
||||
</div>
|
||||
{notes?.length ? (
|
||||
notes?.map((note: NoteMetadata) => (
|
||||
|
|
@ -1,15 +1,15 @@
|
|||
"use client"
|
||||
|
||||
import { useTranslations } from "next-intl"
|
||||
import { usePathname } from "next/navigation"
|
||||
|
||||
import { DashboardMain } from "~/components/dashboard/DashboardMain"
|
||||
import { Image } from "~/components/ui/Image"
|
||||
import { UniLink } from "~/components/ui/UniLink"
|
||||
import { useTranslation } from "~/lib/i18n/client"
|
||||
|
||||
export default function ImportPage() {
|
||||
const pathname = usePathname()
|
||||
const { t } = useTranslation("dashboard")
|
||||
const t = useTranslations()
|
||||
|
||||
const options = [
|
||||
{
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
"use client"
|
||||
|
||||
import { useTranslations } from "next-intl"
|
||||
import {
|
||||
useParams,
|
||||
usePathname,
|
||||
|
|
@ -7,7 +8,6 @@ import {
|
|||
type ReadonlyURLSearchParams,
|
||||
} from "next/navigation"
|
||||
import React, { useEffect } from "react"
|
||||
import { useTranslation } from "react-i18next"
|
||||
|
||||
import { useAccountState, useConnectModal } from "@crossbell/connect-kit"
|
||||
import {
|
||||
|
|
@ -48,7 +48,7 @@ export default function DashboardLayout({
|
|||
const connectModal = useConnectModal()
|
||||
const [ready, setReady] = React.useState(false)
|
||||
const [hasPermission, setHasPermission] = React.useState(false)
|
||||
const { t } = useTranslation("dashboard")
|
||||
const t = useTranslations()
|
||||
|
||||
const isMobileLayout = useIsMobileLayout()
|
||||
const pathname = usePathname()
|
||||
|
|
@ -269,8 +269,8 @@ export default function DashboardLayout({
|
|||
active
|
||||
? `bg-white font-medium text-accent drop-shadow-sm`
|
||||
: link.href || link.onClick
|
||||
? "hover:bg-slate-200 hover:bg-opacity-50"
|
||||
: "opacity-80 cursor-default",
|
||||
? "hover:bg-slate-200 hover:bg-opacity-50"
|
||||
: "opacity-80 cursor-default",
|
||||
)}
|
||||
onClick={link.onClick}
|
||||
>
|
||||
|
|
@ -378,8 +378,8 @@ export default function DashboardLayout({
|
|||
active
|
||||
? `bg-white font-medium text-accent drop-shadow-sm`
|
||||
: link.href || link.onClick
|
||||
? "hover:bg-slate-200 hover:bg-opacity-50"
|
||||
: "opacity-80 cursor-default",
|
||||
? "hover:bg-slate-200 hover:bg-opacity-50"
|
||||
: "opacity-80 cursor-default",
|
||||
)}
|
||||
onClick={link.onClick}
|
||||
>
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
"use client"
|
||||
|
||||
import { useTranslations } from "next-intl"
|
||||
import { useParams } from "next/navigation"
|
||||
|
||||
import { CharacterFloatCard } from "~/components/common/CharacterFloatCard"
|
||||
|
|
@ -15,7 +16,6 @@ import {
|
|||
TWITTER_LINK,
|
||||
} from "~/lib/env"
|
||||
import { getSiteLink } from "~/lib/helpers"
|
||||
import { Trans, useTranslation } from "~/lib/i18n/client"
|
||||
import { cn } from "~/lib/utils"
|
||||
import { useGetShowcase } from "~/queries/home"
|
||||
import { useGetPagesBySite } from "~/queries/page"
|
||||
|
|
@ -30,7 +30,7 @@ export default function SubdomainIndex() {
|
|||
characterId,
|
||||
})
|
||||
const date = useDate()
|
||||
const { t, i18n } = useTranslation("dashboard")
|
||||
const t = useTranslations()
|
||||
const tips = useGetTips({
|
||||
toCharacterId: characterId,
|
||||
limit: 1000,
|
||||
|
|
@ -133,15 +133,9 @@ export default function SubdomainIndex() {
|
|||
))}
|
||||
</div>
|
||||
<div className="prose p-6 bg-slate-50 rounded-lg relative">
|
||||
<Trans
|
||||
i18n={i18n}
|
||||
i18nKey="hello.welcome"
|
||||
defaults="<p>👋 Hello there,</p><p>Welcome to use xLog!</p><p>Here are some useful links to get started:</p>"
|
||||
components={{
|
||||
p: <p />,
|
||||
}}
|
||||
ns="dashboard"
|
||||
/>
|
||||
{t.rich("hello.welcome", {
|
||||
p: (chunks) => <p>{chunks}</p>,
|
||||
})}
|
||||
<ul>
|
||||
<li>
|
||||
<UniLink
|
||||
|
|
@ -163,15 +157,9 @@ export default function SubdomainIndex() {
|
|||
</UniLink>
|
||||
</li>
|
||||
</ul>
|
||||
<Trans
|
||||
i18n={i18n}
|
||||
i18nKey="hello.community"
|
||||
defaults="<p>Join the community to meet friends or build xLog together:</p>"
|
||||
components={{
|
||||
p: <p />,
|
||||
}}
|
||||
ns="dashboard"
|
||||
/>
|
||||
{t.rich("hello.tips", {
|
||||
p: (chunks) => <p>{chunks}</p>,
|
||||
})}
|
||||
<ul
|
||||
style={{
|
||||
marginBottom: 0,
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
"use client"
|
||||
|
||||
import { useTranslations } from "next-intl"
|
||||
import { useParams } from "next/navigation"
|
||||
import { FC, useEffect, useRef, useState } from "react"
|
||||
import toast from "react-hot-toast"
|
||||
|
|
@ -10,7 +11,6 @@ import { Button } from "~/components/ui/Button"
|
|||
import { FieldLabel } from "~/components/ui/FieldLabel"
|
||||
import { useGetState } from "~/hooks/useGetState"
|
||||
import { getSiteLink } from "~/lib/helpers"
|
||||
import { useTranslation } from "~/lib/i18n/client"
|
||||
import { useGetSite, useUpdateSite } from "~/queries/site"
|
||||
|
||||
export default function SettingsCSSPage() {
|
||||
|
|
@ -19,7 +19,7 @@ export default function SettingsCSSPage() {
|
|||
|
||||
const updateSite = useUpdateSite()
|
||||
const site = useGetSite(subdomain)
|
||||
const { t } = useTranslation("dashboard")
|
||||
const t = useTranslations()
|
||||
const [css, setCss] = useState("")
|
||||
const handleSubmit = (e: any) => {
|
||||
e.preventDefault()
|
||||
|
|
@ -55,12 +55,12 @@ export default function SettingsCSSPage() {
|
|||
<p className="text-zinc-800 text-sm font-bold">{t("Tips")}:</p>
|
||||
<p>
|
||||
{t(
|
||||
"Scope: These styles will be applied to your entire blog, including this dashboard.",
|
||||
"Scope: These styles will be applied to your entire blog, including this dashboard",
|
||||
)}
|
||||
</p>
|
||||
<p>
|
||||
{t(
|
||||
"Using a browser plugin that can modify page styles, such as Stylebot, can help with debugging.",
|
||||
"Using a browser plugin that can modify page styles, such as Stylebot, can help with debugging",
|
||||
)}
|
||||
</p>
|
||||
<p>
|
||||
|
|
@ -127,7 +127,7 @@ const PreviewButton: FC<{
|
|||
subdomain: string
|
||||
custom_domain?: string
|
||||
}> = ({ css, subdomain, custom_domain }) => {
|
||||
const { t } = useTranslation("dashboard")
|
||||
const t = useTranslations()
|
||||
|
||||
const datasetRef = useRef({
|
||||
previewWindowOrigin: "",
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
"use client"
|
||||
|
||||
import { useTranslations } from "next-intl"
|
||||
import { useParams, useRouter } from "next/navigation"
|
||||
import { useEffect, useState } from "react"
|
||||
import { useForm } from "react-hook-form"
|
||||
|
|
@ -18,7 +19,6 @@ import { UniLink } from "~/components/ui/UniLink"
|
|||
import { useUserRole } from "~/hooks/useUserRole"
|
||||
import { OUR_DOMAIN } from "~/lib/env"
|
||||
import { getSiteLink } from "~/lib/helpers"
|
||||
import { useTranslation } from "~/lib/i18n/client"
|
||||
import { checkDomain } from "~/models/site.model"
|
||||
import { useGetSite, useUpdateHandle, useUpdateSite } from "~/queries/site"
|
||||
|
||||
|
|
@ -30,7 +30,7 @@ export default function SettingsDomainsPage() {
|
|||
const updateHandle = useUpdateHandle()
|
||||
const site = useGetSite(subdomain)
|
||||
const userRole = useUserRole(subdomain)
|
||||
const { t } = useTranslation("dashboard")
|
||||
const t = useTranslations()
|
||||
const router = useRouter()
|
||||
|
||||
const isEmailAccount = useAccountState(
|
||||
|
|
@ -194,8 +194,8 @@ export default function SettingsDomainsPage() {
|
|||
>
|
||||
{t(
|
||||
subdomainCheckResult.data
|
||||
? "Subdomain Available."
|
||||
: subdomainCheckResult.error || "Subdomain Unavailable.",
|
||||
? "Subdomain Available"
|
||||
: subdomainCheckResult.error || "Subdomain Unavailable",
|
||||
)}
|
||||
</span>
|
||||
)}
|
||||
|
|
@ -274,12 +274,12 @@ export default function SettingsDomainsPage() {
|
|||
<span>{t("DNS Checking")}...</span>
|
||||
) : domainCheckResult.data ? (
|
||||
<span className="text-green-600">
|
||||
{t("DNS check passed.")}
|
||||
{t("DNS check passed")}
|
||||
</span>
|
||||
) : (
|
||||
<span>
|
||||
<span className="text-red-600">
|
||||
{t("DNS check failed.")}
|
||||
{t("DNS check failed")}
|
||||
</span>
|
||||
<Button
|
||||
className="ml-4 font-media"
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
"use client"
|
||||
|
||||
import { useTranslations } from "next-intl"
|
||||
import { useParams } from "next/navigation"
|
||||
import { useEffect, useState } from "react"
|
||||
import { Controller, useForm } from "react-hook-form"
|
||||
|
|
@ -10,7 +11,6 @@ import { Button } from "~/components/ui/Button"
|
|||
import { ImageUploader } from "~/components/ui/ImageUploader"
|
||||
import { Input } from "~/components/ui/Input"
|
||||
import { UniLink } from "~/components/ui/UniLink"
|
||||
import { Trans, useTranslation } from "~/lib/i18n/client"
|
||||
import { toIPFS } from "~/lib/ipfs-parser"
|
||||
import { useGetSite, useUpdateSite } from "~/queries/site"
|
||||
|
||||
|
|
@ -20,7 +20,7 @@ export default function SiteSettingsGeneralPage() {
|
|||
|
||||
const updateSite = useUpdateSite()
|
||||
const site = useGetSite(subdomain)
|
||||
const { t, i18n } = useTranslation("dashboard")
|
||||
const t = useTranslations()
|
||||
|
||||
const form = useForm({
|
||||
defaultValues: {
|
||||
|
|
@ -157,7 +157,7 @@ export default function SiteSettingsGeneralPage() {
|
|||
)}
|
||||
/>
|
||||
<div className="text-xs text-gray-400 mt-1">
|
||||
{t("Supports both pictures and videos.")}
|
||||
{t("Supports both pictures and videos")}
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-5">
|
||||
|
|
@ -175,7 +175,7 @@ export default function SiteSettingsGeneralPage() {
|
|||
placeholder={form.getValues("name")}
|
||||
{...form.register("site_name")}
|
||||
help={t(
|
||||
"It will appear in the page title and site header, using the character name by default.",
|
||||
"It will appear in the page title and site header, using the character name by default",
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -199,21 +199,16 @@ export default function SiteSettingsGeneralPage() {
|
|||
label="Google Analytics"
|
||||
help={
|
||||
<p>
|
||||
<Trans
|
||||
i18n={i18n}
|
||||
i18nKey="Integrate Google Analytics"
|
||||
ns="dashboard"
|
||||
>
|
||||
Integrate Google Analytics into your site. You can follow the
|
||||
instructions{" "}
|
||||
<UniLink
|
||||
className="underline"
|
||||
href="https://support.google.com/analytics/answer/9539598"
|
||||
>
|
||||
here
|
||||
</UniLink>{" "}
|
||||
to find your Measurement ID.
|
||||
</Trans>
|
||||
{t.rich("Integrate Google Analytics", {
|
||||
link: (chunks) => (
|
||||
<UniLink
|
||||
className="underline"
|
||||
href="https://support.google.com/analytics/answer/9539598"
|
||||
>
|
||||
{chunks}
|
||||
</UniLink>
|
||||
),
|
||||
})}
|
||||
</p>
|
||||
}
|
||||
/>
|
||||
|
|
@ -227,16 +222,16 @@ export default function SiteSettingsGeneralPage() {
|
|||
label="Umami Cloud Analytics"
|
||||
help={
|
||||
<p>
|
||||
<Trans
|
||||
i18nKey="Integrate Umami Cloud Analytics (set url)"
|
||||
ns="dashboard"
|
||||
>
|
||||
Leave blank to use{" "}
|
||||
<UniLink className="underline" href="https://cloud.umami.is">
|
||||
the Umami Cloud
|
||||
</UniLink>
|
||||
.
|
||||
</Trans>
|
||||
{t.rich("Integrate Umami Cloud Analytics (set url)", {
|
||||
link: (chunks) => (
|
||||
<UniLink
|
||||
className="underline"
|
||||
href="https://cloud.umami.is"
|
||||
>
|
||||
{chunks}
|
||||
</UniLink>
|
||||
),
|
||||
})}
|
||||
</p>
|
||||
}
|
||||
/>
|
||||
|
|
@ -247,21 +242,16 @@ export default function SiteSettingsGeneralPage() {
|
|||
placeholder="xxxxxxxx-xxxx-..."
|
||||
help={
|
||||
<p>
|
||||
<Trans
|
||||
i18n={i18n}
|
||||
i18nKey="Integrate Umami Cloud Analytics"
|
||||
ns="dashboard"
|
||||
>
|
||||
Integrate Umami Cloud Analytics into your site. You can follow
|
||||
the instructions{" "}
|
||||
<UniLink
|
||||
className="underline"
|
||||
href="https://umami.is/docs/collect-data"
|
||||
>
|
||||
here
|
||||
</UniLink>{" "}
|
||||
to find your Website ID.
|
||||
</Trans>
|
||||
{t.rich("Integrate Umami Cloud Analytics", {
|
||||
link: (chunks) => (
|
||||
<UniLink
|
||||
className="underline"
|
||||
href="https://umami.is/docs/collect-data"
|
||||
>
|
||||
{chunks}
|
||||
</UniLink>
|
||||
),
|
||||
})}
|
||||
</p>
|
||||
}
|
||||
/>
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
import equal from "fast-deep-equal"
|
||||
import { nanoid } from "nanoid"
|
||||
import { useTranslations } from "next-intl"
|
||||
import { useParams } from "next/navigation"
|
||||
import { ChangeEvent, FormEvent, useEffect, useMemo, useState } from "react"
|
||||
import toast from "react-hot-toast"
|
||||
|
|
@ -10,7 +11,6 @@ import { ReactSortable } from "react-sortablejs"
|
|||
import { SettingsLayout } from "~/components/dashboard/SettingsLayout"
|
||||
import { Button } from "~/components/ui/Button"
|
||||
import { Input } from "~/components/ui/Input"
|
||||
import { useTranslation } from "~/lib/i18n/client"
|
||||
import { SiteNavigationItem } from "~/lib/types"
|
||||
import { useGetSite, useUpdateSite } from "~/queries/site"
|
||||
|
||||
|
|
@ -27,7 +27,7 @@ const SortableNavigationItem = ({
|
|||
updateItem: UpdateItem
|
||||
removeItem: RemoveItem
|
||||
}) => {
|
||||
const { t } = useTranslation("dashboard")
|
||||
const t = useTranslations()
|
||||
return (
|
||||
<div className="flex space-x-5 border-b p-5 bg-zinc-50 last:border-0">
|
||||
<div>
|
||||
|
|
@ -74,7 +74,7 @@ export default function SiteSettingsNavigationPage() {
|
|||
|
||||
const updateSite = useUpdateSite()
|
||||
const site = useGetSite(subdomain)
|
||||
const { t } = useTranslation("dashboard")
|
||||
const t = useTranslations()
|
||||
|
||||
const [items, setItems] = useState<SiteNavigationItem[]>([])
|
||||
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
"use client"
|
||||
|
||||
import { useTranslations } from "next-intl"
|
||||
import { useParams } from "next/navigation"
|
||||
import { useEffect, useState } from "react"
|
||||
import toast from "react-hot-toast"
|
||||
|
|
@ -17,7 +18,6 @@ import { Input } from "~/components/ui/Input"
|
|||
import { UniLink } from "~/components/ui/UniLink"
|
||||
import { useUserRole } from "~/hooks/useUserRole"
|
||||
import { getSiteLink } from "~/lib/helpers"
|
||||
import { useTranslation } from "~/lib/i18n/client"
|
||||
import {
|
||||
useAddOperator,
|
||||
useGetOperators,
|
||||
|
|
@ -38,7 +38,7 @@ const SortableNavigationItem = ({
|
|||
isLoading: boolean
|
||||
disabled?: boolean
|
||||
}) => {
|
||||
const { t } = useTranslation("dashboard")
|
||||
const t = useTranslations()
|
||||
return (
|
||||
<div className="flex space-x-5 border-b p-5 bg-zinc-50 last:border-0 items-center">
|
||||
<div className="text-sm space-y-4">
|
||||
|
|
@ -82,7 +82,7 @@ export default function SettingsOperatorPage() {
|
|||
)
|
||||
const upgradeAccountModal = useUpgradeEmailAccountModal()
|
||||
const userRole = useUserRole(subdomain)
|
||||
const { t } = useTranslation("dashboard")
|
||||
const t = useTranslations()
|
||||
|
||||
const [items, setItems] = useState<string[]>([])
|
||||
|
||||
|
|
@ -215,7 +215,7 @@ export default function SettingsOperatorPage() {
|
|||
{!isEmailAccount && userRole.data !== "operator" && (
|
||||
<span>
|
||||
{t(
|
||||
"Operators have permissions to enter your dashboard, change your settings(excluding xLog subdomain) and post, modify, delete contents on your site.",
|
||||
"Operators have permissions to enter your dashboard, change your settings(excluding xLog subdomain) and post, modify, delete contents on your site",
|
||||
)}
|
||||
</span>
|
||||
)}
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
import equal from "fast-deep-equal"
|
||||
import { nanoid } from "nanoid"
|
||||
import { useTranslations } from "next-intl"
|
||||
import { useParams } from "next/navigation"
|
||||
import { ChangeEvent, FormEvent, useEffect, useMemo, useState } from "react"
|
||||
import toast from "react-hot-toast"
|
||||
|
|
@ -12,7 +13,6 @@ import { Platform, PlatformsSyncMap } from "~/components/site/Platform"
|
|||
import { Button } from "~/components/ui/Button"
|
||||
import { Input } from "~/components/ui/Input"
|
||||
import { UniLink } from "~/components/ui/UniLink"
|
||||
import { Trans, useTranslation } from "~/lib/i18n/client"
|
||||
import { useGetSite, useUpdateSite } from "~/queries/site"
|
||||
|
||||
type Item = {
|
||||
|
|
@ -36,7 +36,7 @@ const SortableNavigationItem = ({
|
|||
updateItem: UpdateItem
|
||||
removeItem: RemoveItem
|
||||
}) => {
|
||||
const { t } = useTranslation("dashboard")
|
||||
const t = useTranslations()
|
||||
return (
|
||||
<div className="flex space-x-5 border-b p-5 bg-zinc-50 last:border-0">
|
||||
<div>
|
||||
|
|
@ -86,7 +86,7 @@ export default function SiteSettingsNavigationPage() {
|
|||
|
||||
const updateSite = useUpdateSite()
|
||||
const site = useGetSite(subdomain)
|
||||
const { t, i18n } = useTranslation("dashboard")
|
||||
const t = useTranslations()
|
||||
|
||||
const [items, setItems] = useState<Item[]>([])
|
||||
|
||||
|
|
@ -169,18 +169,16 @@ export default function SiteSettingsNavigationPage() {
|
|||
</p>
|
||||
<p>
|
||||
<span className="text-zinc-800">
|
||||
<Trans i18n={i18n} ns="dashboard" i18nKey="social tips.p2">
|
||||
We support{" "}
|
||||
<UniLink
|
||||
href="https://github.com/Crossbell-Box/xLog/blob/dev/src/components/site/Platform.tsx#L11"
|
||||
className="underline"
|
||||
>
|
||||
these platforms
|
||||
</UniLink>{" "}
|
||||
with automatic display of logos and links, other platforms will
|
||||
display a default logo. Please feel free to submit an issue or pr
|
||||
to us to support more platforms.
|
||||
</Trans>
|
||||
{t.rich("social tips.p2", {
|
||||
link: (chunks) => (
|
||||
<UniLink
|
||||
href="https://github.com/Crossbell-Box/xLog/blob/dev/src/components/site/Platform.tsx#L11"
|
||||
className="underline"
|
||||
>
|
||||
(chunks)
|
||||
</UniLink>
|
||||
),
|
||||
})}
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
"use client"
|
||||
|
||||
import { useTranslations } from "next-intl"
|
||||
import { useParams } from "next/navigation"
|
||||
|
||||
import {
|
||||
|
|
@ -13,13 +14,11 @@ import { Button } from "~/components/ui/Button"
|
|||
import { UniLink } from "~/components/ui/UniLink"
|
||||
import { MIRA_LINK } from "~/lib/env"
|
||||
import { getSiteLink } from "~/lib/helpers"
|
||||
import { useTranslation } from "~/lib/i18n/client"
|
||||
import { useGetMiraBalance, useGetSite } from "~/queries/site"
|
||||
|
||||
export default function TokensPage() {
|
||||
const params = useParams()
|
||||
const { t } = useTranslation("dashboard")
|
||||
const { t: indexT } = useTranslation("index")
|
||||
const t = useTranslations()
|
||||
const subdomain = params?.subdomain as string
|
||||
const site = useGetSite(subdomain)
|
||||
|
||||
|
|
@ -38,11 +37,11 @@ export default function TokensPage() {
|
|||
<>
|
||||
<p>
|
||||
{t(
|
||||
"$MIRA is a valuable token in the Crossbell world, and can be easily exchanged on the Crosschain Bridge and Uniswap.",
|
||||
"$MIRA is a valuable token in the Crossbell world, and can be easily exchanged on the Crosschain Bridge and Uniswap",
|
||||
)}
|
||||
</p>
|
||||
<p>
|
||||
{t("In the early stage, xLog will use $MIRA to motivate creators.")}
|
||||
{t("In the early stage, xLog will use $MIRA to motivate creators")}
|
||||
</p>
|
||||
<p>{t("You can obtain $MIRA through the following ways:")}</p>
|
||||
<ul className="ml-2">
|
||||
|
|
@ -54,7 +53,7 @@ export default function TokensPage() {
|
|||
subdomain: "xlog",
|
||||
})}/creator-incentive-plan`}
|
||||
>
|
||||
{t("Creator incentive program.")}
|
||||
{t("Creator incentive program")}
|
||||
</UniLink>
|
||||
</li>
|
||||
<li>
|
||||
|
|
@ -63,16 +62,16 @@ export default function TokensPage() {
|
|||
className="underline"
|
||||
href={`/dashboard/${subdomain}/events`}
|
||||
>
|
||||
{t("Participate in events.")}
|
||||
{t("Participate in events")}
|
||||
</UniLink>
|
||||
</li>
|
||||
<li>
|
||||
3.{" "}
|
||||
<UniLink className="underline" href={MIRA_LINK}>
|
||||
{t("Swap from USDC.")}
|
||||
{t("Swap from USDC")}
|
||||
</UniLink>
|
||||
</li>
|
||||
<li>4. {t("Received tips and sponsorships from readers.")}</li>
|
||||
<li>4. {t("Received tips and sponsorships from readers")}</li>
|
||||
</ul>
|
||||
</>
|
||||
),
|
||||
|
|
@ -96,7 +95,7 @@ export default function TokensPage() {
|
|||
description: (
|
||||
<>
|
||||
{t(
|
||||
"This is a token used for interaction on the Crossbell blockchain, which can be claimed for free from the faucet, so there's no need to worry about its balance.",
|
||||
"This is a token used for interaction on the Crossbell blockchain, which can be claimed for free from the faucet, so there's no need to worry about its balance",
|
||||
)}
|
||||
</>
|
||||
),
|
||||
|
|
@ -114,7 +113,7 @@ export default function TokensPage() {
|
|||
{
|
||||
name: "XLOG",
|
||||
description: t(
|
||||
"$XLOG is related to xLog DAO, but it's too early now, please stay tuned.",
|
||||
"$XLOG is related to xLog DAO, but it's too early now, please stay tuned",
|
||||
),
|
||||
balance: t("Stay tuned"),
|
||||
},
|
||||
|
|
@ -124,7 +123,7 @@ export default function TokensPage() {
|
|||
<DashboardMain title="Tokens">
|
||||
<div className="min-w-[270px] max-w-screen-lg flex flex-col space-y-8">
|
||||
<div className="text-sm text-zinc-500 leading-relaxed">
|
||||
{indexT("features.Earn.description")}
|
||||
{t("features.Earn.description")}
|
||||
</div>
|
||||
{tokens.map((token) => {
|
||||
return (
|
||||
|
|
@ -1,13 +1,14 @@
|
|||
import { dir } from "i18next"
|
||||
import { Metadata } from "next"
|
||||
import { NextIntlClientProvider } from "next-intl"
|
||||
import { getMessages, unstable_setRequestLocale } from "next-intl/server"
|
||||
import { headers } from "next/headers"
|
||||
import { notFound } from "next/navigation"
|
||||
import Script from "next/script"
|
||||
import { Toaster } from "react-hot-toast"
|
||||
|
||||
import { updateIndexerFetchOptions } from "@crossbell/indexer"
|
||||
import { ColorSchemeScript, MantineProvider } from "@mantine/core"
|
||||
|
||||
import { getAcceptLang } from "~/lib/accept-lang"
|
||||
import {
|
||||
APP_DESCRIPTION,
|
||||
APP_NAME,
|
||||
|
|
@ -92,14 +93,22 @@ export const metadata: Metadata = {
|
|||
},
|
||||
}
|
||||
|
||||
export default function RootLayout({
|
||||
export default async function RootLayout({
|
||||
children,
|
||||
modal,
|
||||
params,
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
modal: React.ReactNode
|
||||
params?: {
|
||||
locale: string
|
||||
}
|
||||
}) {
|
||||
const lang = getAcceptLang()
|
||||
if (!params?.locale) {
|
||||
return notFound()
|
||||
}
|
||||
|
||||
// const lang = getAcceptLang()
|
||||
const colorScheme = getColorScheme()
|
||||
|
||||
// For viewing statistics
|
||||
|
|
@ -112,22 +121,27 @@ export default function RootLayout({
|
|||
})
|
||||
}
|
||||
|
||||
unstable_setRequestLocale(params.locale)
|
||||
const messages = await getMessages()
|
||||
|
||||
return (
|
||||
<html lang={lang} dir={dir(lang)} className={colorScheme}>
|
||||
<html lang={params.locale} className={colorScheme}>
|
||||
<head>
|
||||
<ColorSchemeInjector />
|
||||
<ColorSchemeScript />
|
||||
</head>
|
||||
<body>
|
||||
<MantineProvider
|
||||
theme={mantineTheme}
|
||||
defaultColorScheme={mantineDefaultColorScheme}
|
||||
>
|
||||
<Providers lang={lang}>
|
||||
{modal}
|
||||
{children}
|
||||
</Providers>
|
||||
</MantineProvider>
|
||||
<NextIntlClientProvider messages={messages}>
|
||||
<MantineProvider
|
||||
theme={mantineTheme}
|
||||
defaultColorScheme={mantineDefaultColorScheme}
|
||||
>
|
||||
<Providers>
|
||||
{modal}
|
||||
{children}
|
||||
</Providers>
|
||||
</MantineProvider>
|
||||
</NextIntlClientProvider>
|
||||
<Toaster />
|
||||
<Script
|
||||
id="xlog-umami-analytics"
|
||||
|
|
@ -1,12 +1,12 @@
|
|||
"use client"
|
||||
|
||||
import { useTranslations } from "next-intl"
|
||||
import { useCallback, useEffect } from "react"
|
||||
|
||||
import { Image } from "~/components/ui/Image"
|
||||
import { useTranslation } from "~/lib/i18n/client"
|
||||
|
||||
function OIAPage() {
|
||||
const { t } = useTranslation("site")
|
||||
const t = useTranslations()
|
||||
|
||||
useEffect(() => {
|
||||
document.body.style.backgroundColor = "#fb9148"
|
||||
|
|
@ -1,5 +1,13 @@
|
|||
"use client"
|
||||
|
||||
import {
|
||||
IntlError,
|
||||
IntlErrorCode,
|
||||
NextIntlClientProvider,
|
||||
useLocale,
|
||||
useMessages,
|
||||
useTimeZone,
|
||||
} from "next-intl"
|
||||
import { ThemeProvider } from "next-themes"
|
||||
import { useState } from "react"
|
||||
import { WagmiConfig } from "wagmi"
|
||||
|
|
@ -22,7 +30,6 @@ import { APP_NAME, WALLET_CONNECT_V2_PROJECT_ID } from "~/lib/env"
|
|||
import { filterNotificationCharacter } from "~/lib/filter-character"
|
||||
import { toGateway } from "~/lib/ipfs-parser"
|
||||
import { urlComposer } from "~/lib/url-composer"
|
||||
import { LangProvider } from "~/providers/LangProvider"
|
||||
|
||||
const wagmiConfig = createWagmiConfig({
|
||||
appName: APP_NAME,
|
||||
|
|
@ -54,44 +61,51 @@ export const mantineDefaultColorScheme = mantineDefaultColorSchemeT as
|
|||
| "light"
|
||||
| "dark"
|
||||
|
||||
export default function Providers({
|
||||
children,
|
||||
lang,
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
lang: string
|
||||
}) {
|
||||
export default function Providers({ children }: { children: React.ReactNode }) {
|
||||
useMobileLayout()
|
||||
useNProgress()
|
||||
useDarkModeListener()
|
||||
|
||||
const [queryClient] = useState(() => new QueryClient())
|
||||
|
||||
return (
|
||||
<ThemeProvider
|
||||
disableTransitionOnChange
|
||||
storageKey={DARK_MODE_STORAGE_KEY}
|
||||
attribute="class"
|
||||
>
|
||||
<WagmiConfig config={wagmiConfig}>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<ConnectKitProvider
|
||||
ipfsLinkToHttpLink={toGateway}
|
||||
urlComposer={urlComposer}
|
||||
signInStrategy="simple"
|
||||
ignoreWalletDisconnectEvent={true}
|
||||
>
|
||||
<LangProvider lang={lang}>
|
||||
<ModalStackProvider>{children}</ModalStackProvider>
|
||||
</LangProvider>
|
||||
const messages = useMessages()
|
||||
const locale = useLocale()
|
||||
const timeZone = useTimeZone()
|
||||
const onIntlError = (error: IntlError) => {
|
||||
if (error.code !== IntlErrorCode.MISSING_MESSAGE) {
|
||||
console.log(`Intl error`, error)
|
||||
}
|
||||
}
|
||||
|
||||
<NotificationModal
|
||||
colorScheme={colorScheme}
|
||||
filter={filterNotificationCharacter}
|
||||
/>
|
||||
</ConnectKitProvider>
|
||||
</QueryClientProvider>
|
||||
</WagmiConfig>
|
||||
</ThemeProvider>
|
||||
return (
|
||||
<NextIntlClientProvider
|
||||
onError={onIntlError}
|
||||
locale={locale}
|
||||
messages={messages}
|
||||
timeZone={timeZone}
|
||||
>
|
||||
<ThemeProvider
|
||||
disableTransitionOnChange
|
||||
storageKey={DARK_MODE_STORAGE_KEY}
|
||||
attribute="class"
|
||||
>
|
||||
<WagmiConfig config={wagmiConfig}>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<ConnectKitProvider
|
||||
ipfsLinkToHttpLink={toGateway}
|
||||
urlComposer={urlComposer}
|
||||
signInStrategy="simple"
|
||||
ignoreWalletDisconnectEvent={true}
|
||||
>
|
||||
<ModalStackProvider>{children}</ModalStackProvider>
|
||||
<NotificationModal
|
||||
colorScheme={colorScheme}
|
||||
filter={filterNotificationCharacter}
|
||||
/>
|
||||
</ConnectKitProvider>
|
||||
</QueryClientProvider>
|
||||
</WagmiConfig>
|
||||
</ThemeProvider>
|
||||
</NextIntlClientProvider>
|
||||
)
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
import { Metadata } from "next"
|
||||
import { getLocale, getTranslations } from "next-intl/server"
|
||||
import { notFound } from "next/navigation"
|
||||
import serialize from "serialize-javascript"
|
||||
|
||||
|
|
@ -11,7 +12,6 @@ import { PostFooter } from "~/components/site/PostFooter"
|
|||
import PostMeta from "~/components/site/PostMeta"
|
||||
import { SITE_URL } from "~/lib/env"
|
||||
import { getSiteLink } from "~/lib/helpers"
|
||||
import { getTranslation } from "~/lib/i18n"
|
||||
import { toCid, toGateway } from "~/lib/ipfs-parser"
|
||||
import { isInRN } from "~/lib/is-in-rn"
|
||||
import { isOnlyContent } from "~/lib/is-only-content"
|
||||
|
|
@ -132,13 +132,13 @@ export default async function SitePagePage({
|
|||
}
|
||||
}
|
||||
|
||||
const { i18n } = await getTranslation()
|
||||
const { t } = await getTranslation("common")
|
||||
const locale = await getLocale()
|
||||
const t = await getTranslations()
|
||||
let summary: string | undefined
|
||||
if (!page.metadata.content.disableAISummary) {
|
||||
summary = await getSummary({
|
||||
cid: toCid(page.metadata?.uri || ""),
|
||||
lang: i18n.resolvedLanguage,
|
||||
lang: locale,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
import { Metadata } from "next"
|
||||
import { getTranslations } from "next-intl/server"
|
||||
|
||||
import { Image } from "~/components/ui/Image"
|
||||
import { UniLink } from "~/components/ui/UniLink"
|
||||
import { getTranslation } from "~/lib/i18n"
|
||||
import getQueryClient from "~/lib/query-client"
|
||||
import { fetchGetSite, getNFTs } from "~/queries/site.server"
|
||||
|
||||
|
|
@ -34,7 +34,7 @@ export default async function SiteNFTPage({
|
|||
const queryClient = getQueryClient()
|
||||
|
||||
const site = await fetchGetSite(params.site, queryClient)
|
||||
const { t } = await getTranslation("common")
|
||||
const t = await getTranslations()
|
||||
|
||||
let nfts = (await getNFTs(site?.owner)) ?? {
|
||||
nfts: [],
|
||||
|
|
@ -1,16 +1,17 @@
|
|||
import { getTranslations } from "next-intl/server"
|
||||
|
||||
import { Image } from "~/components/ui/Image"
|
||||
import { SITE_URL } from "~/lib/env"
|
||||
import { getTranslation } from "~/lib/i18n"
|
||||
|
||||
export default async function NotFound() {
|
||||
const { t } = await getTranslation("site")
|
||||
const t = await getTranslations()
|
||||
|
||||
return (
|
||||
<>
|
||||
<article>
|
||||
<div>
|
||||
<h2 className="xlog-post-title text-xl font-bold page-title text-center">
|
||||
{t("404 - Whoops, this page is gone.")}
|
||||
{t("404 - Whoops, this page is gone")}
|
||||
</h2>
|
||||
</div>
|
||||
<div className="mt-10 xlog-post-content prose">
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
"use client"
|
||||
|
||||
import { useTranslations } from "next-intl"
|
||||
import dynamic from "next/dynamic"
|
||||
import { useParams } from "next/navigation"
|
||||
|
||||
import { useTranslation } from "~/lib/i18n/client"
|
||||
import { useGetPage } from "~/queries/page"
|
||||
import { useGetSite } from "~/queries/site"
|
||||
|
||||
|
|
@ -28,13 +28,13 @@ export default function SitePreviewPage() {
|
|||
useStat: true,
|
||||
})
|
||||
|
||||
const { t } = useTranslation("site")
|
||||
const t = useTranslations()
|
||||
|
||||
return (
|
||||
<div className="max-w-screen-md mx-auto">
|
||||
<div className="fixed top-0 left-0 w-full text-center text-red-500 bg-gray-100 py-2 opacity-80 text-sm z-10">
|
||||
{t(
|
||||
"This address is in local editing preview mode and cannot be viewed by the public.",
|
||||
"This address is in local editing preview mode and cannot be viewed by the public",
|
||||
)}
|
||||
</div>
|
||||
<article>
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
import { useTranslations } from "next-intl"
|
||||
import { useState } from "react"
|
||||
|
||||
import { Indicator } from "@mantine/core"
|
||||
|
|
@ -5,7 +6,6 @@ import { Indicator } from "@mantine/core"
|
|||
import { AchievementModal } from "~/components/common/AchievementModal"
|
||||
import { Image } from "~/components/ui/Image"
|
||||
import { useDate } from "~/hooks/useDate"
|
||||
import { useTranslation } from "~/lib/i18n/client"
|
||||
import type { AchievementSection } from "~/models/site.model"
|
||||
|
||||
export const Badge = ({
|
||||
|
|
@ -48,7 +48,7 @@ export const AchievementItem = ({
|
|||
isOwner: boolean
|
||||
}) => {
|
||||
const date = useDate()
|
||||
const { t } = useTranslation("common")
|
||||
const t = useTranslations()
|
||||
|
||||
const achievement = group.items
|
||||
.filter((item) => item.status === "MINTED")
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { useTranslations } from "next-intl"
|
||||
import Tilt from "react-parallax-tilt"
|
||||
|
||||
import { Modal, Stepper } from "@mantine/core"
|
||||
|
|
@ -7,7 +8,6 @@ import { BlockchainIcon } from "~/components/icons/BlockchainIcon"
|
|||
import { Button } from "~/components/ui/Button"
|
||||
import { Image } from "~/components/ui/Image"
|
||||
import { useDate } from "~/hooks/useDate"
|
||||
import { useTranslation } from "~/lib/i18n/client"
|
||||
import { cn } from "~/lib/utils"
|
||||
import type { AchievementSection } from "~/models/site.model"
|
||||
import { useMintAchievement } from "~/queries/site"
|
||||
|
|
@ -28,7 +28,7 @@ export const AchievementModal = ({
|
|||
characterId?: number
|
||||
}) => {
|
||||
const date = useDate()
|
||||
const { t } = useTranslation("common")
|
||||
const t = useTranslations()
|
||||
|
||||
const achievement = group.items
|
||||
.filter((item) => item.status === "MINTED")
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
"use client"
|
||||
|
||||
import { useTranslations } from "next-intl"
|
||||
import { useEffect, useState } from "react"
|
||||
|
||||
import { BlockchainIcon } from "~/components/icons/BlockchainIcon"
|
||||
import { CSB_SCAN, SITE_URL } from "~/lib/env"
|
||||
import { useTranslation } from "~/lib/i18n/client"
|
||||
import { toGateway, toIPFS } from "~/lib/ipfs-parser"
|
||||
import { ExpandedCharacter, ExpandedNote } from "~/lib/types"
|
||||
import { cn } from "~/lib/utils"
|
||||
|
|
@ -17,7 +17,7 @@ export const BlockchainInfo = ({
|
|||
site?: ExpandedCharacter
|
||||
page?: ExpandedNote
|
||||
}) => {
|
||||
const { t } = useTranslation("common")
|
||||
const t = useTranslations()
|
||||
|
||||
const ipfs = (page ? page.metadata?.uri : site?.metadata?.uri) || ""
|
||||
|
||||
|
|
@ -83,7 +83,7 @@ export const BlockchainInfo = ({
|
|||
<span className="ml-2">
|
||||
{!!blockNumber &&
|
||||
!!realBlockNumber &&
|
||||
`(${t("Confirmed by {{blockNumber}} blocks", {
|
||||
`(${t("Confirmed by {blockNumber} blocks", {
|
||||
blockNumber:
|
||||
realBlockNumber -
|
||||
(page?.blockNumber || site?.blockNumber || 0),
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
import { useTranslations } from "next-intl"
|
||||
|
||||
import { FollowingButton } from "~/components/common/FollowingButton"
|
||||
import { FollowingCount } from "~/components/common/FollowingCount"
|
||||
import { Titles } from "~/components/common/Titles"
|
||||
import { Avatar } from "~/components/ui/Avatar"
|
||||
import { Skeleton } from "~/components/ui/Skeleton"
|
||||
import { useDate } from "~/hooks/useDate"
|
||||
import { useTranslation } from "~/lib/i18n/client"
|
||||
import { cn } from "~/lib/utils"
|
||||
import { useGetCharacterCard } from "~/queries/site"
|
||||
|
||||
|
|
@ -26,7 +27,7 @@ export const CharacterCard = ({
|
|||
style?: "flat" | "normal"
|
||||
}) => {
|
||||
const date = useDate()
|
||||
const { t } = useTranslation("common")
|
||||
const t = useTranslations()
|
||||
|
||||
const { data: site } = useGetCharacterCard({
|
||||
siteId,
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import { useTranslations } from "next-intl"
|
||||
import React, { useCallback, useState } from "react"
|
||||
import { Virtuoso } from "react-virtuoso"
|
||||
|
||||
import { Modal } from "~/components/ui/Modal"
|
||||
import { useTranslation } from "~/lib/i18n/client"
|
||||
import { ExpandedCharacter } from "~/lib/types"
|
||||
|
||||
import { Button } from "../ui/Button"
|
||||
|
|
@ -26,7 +26,7 @@ export const CharacterList = ({
|
|||
} | null)[]
|
||||
title: string
|
||||
}) => {
|
||||
const { t } = useTranslation("common")
|
||||
const t = useTranslations()
|
||||
const flattenList = list?.reduce(
|
||||
(acc, cur) => acc.concat(cur?.list || []),
|
||||
[] as any[],
|
||||
|
|
@ -76,7 +76,7 @@ export const CharacterList = ({
|
|||
) : (
|
||||
<div className="px-5 overflow-auto flex-1">
|
||||
<div className="py-3 text-center text-zinc-300">
|
||||
{t("No Content Yet.")}
|
||||
{t("No Content Yet")}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
|
@ -91,7 +91,7 @@ export const CharacterList = ({
|
|||
}
|
||||
|
||||
const Loader = () => {
|
||||
const { t } = useTranslation("common")
|
||||
const t = useTranslations()
|
||||
return (
|
||||
<div className="text-sm py-3 text-center" key={0}>
|
||||
{t("Loading")} ...
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
"use client"
|
||||
|
||||
import { useTranslations } from "next-intl"
|
||||
import { useState } from "react"
|
||||
// TODO
|
||||
import { Virtuoso } from "react-virtuoso"
|
||||
|
||||
import { CommentInput } from "~/components/common/CommentInput"
|
||||
import { CommentItem } from "~/components/common/CommentItem"
|
||||
import { useTranslation } from "~/lib/i18n/client"
|
||||
import { ExpandedNote } from "~/lib/types"
|
||||
import { cn } from "~/lib/utils"
|
||||
import { useGetComments } from "~/queries/page"
|
||||
|
|
@ -24,7 +24,7 @@ const Comment = ({
|
|||
characterId: page?.characterId,
|
||||
noteId: page?.noteId,
|
||||
})
|
||||
const { t } = useTranslation("common")
|
||||
const t = useTranslations()
|
||||
|
||||
const data = comments.data?.pages.filter(
|
||||
(page) => (page?.list?.length ?? 0) > 0,
|
||||
|
|
@ -99,7 +99,7 @@ const Comment = ({
|
|||
}
|
||||
|
||||
const Loader = () => {
|
||||
const { t } = useTranslation("common")
|
||||
const t = useTranslations()
|
||||
return (
|
||||
<div
|
||||
className="relative mt-4 w-full text-sm text-center py-4"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import type { CharacterEntity, NoteEntity } from "crossbell"
|
||||
import { nanoid } from "nanoid"
|
||||
import { useTranslations } from "next-intl"
|
||||
import { useCallback, useEffect, useRef, useState } from "react"
|
||||
import { useForm } from "react-hook-form"
|
||||
|
||||
|
|
@ -12,7 +13,6 @@ import { Avatar } from "~/components/ui/Avatar"
|
|||
import { Button } from "~/components/ui/Button"
|
||||
import CodeMirrorEditor from "~/components/ui/CodeMirror"
|
||||
import { useUploadFile } from "~/hooks/useUploadFile"
|
||||
import { useTranslation } from "~/lib/i18n/client"
|
||||
import {
|
||||
useAnonymousComment,
|
||||
useCommentPage,
|
||||
|
|
@ -44,7 +44,7 @@ export const CommentInput = ({
|
|||
const account = useAccountState((s) => s.computed.account)
|
||||
const commentPage = useCommentPage()
|
||||
const updateComment = useUpdateComment()
|
||||
const { t } = useTranslation("site")
|
||||
const t = useTranslations()
|
||||
const anonymousComment = useAnonymousComment()
|
||||
const [anonymous, setAnonymous] = useState(false)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
"use client"
|
||||
|
||||
import type { CharacterEntity, NoteEntity } from "crossbell"
|
||||
import { useTranslations } from "next-intl"
|
||||
import { useEffect, useState } from "react"
|
||||
|
||||
import { useAccountState } from "@crossbell/connect-kit"
|
||||
|
|
@ -19,7 +20,6 @@ import { UniLink } from "~/components/ui/UniLink"
|
|||
import { useDate } from "~/hooks/useDate"
|
||||
import { CSB_SCAN } from "~/lib/env"
|
||||
import { getSiteLink } from "~/lib/helpers"
|
||||
import { useTranslation } from "~/lib/i18n/client"
|
||||
import { cn } from "~/lib/utils"
|
||||
import { useDeletePage } from "~/queries/page"
|
||||
|
||||
|
|
@ -42,7 +42,7 @@ export const CommentItem = ({
|
|||
const [editOpen, setEditOpen] = useState(false)
|
||||
const [deleteConfirmModalOpen, setDeleteConfirmModalOpen] = useState(false)
|
||||
|
||||
const { t } = useTranslation("common")
|
||||
const t = useTranslations()
|
||||
const date = useDate()
|
||||
const queryClient = useQueryClient()
|
||||
const deletePage = useDeletePage()
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
"use client"
|
||||
|
||||
import { useTranslations } from "next-intl"
|
||||
import React, { useEffect, useState } from "react"
|
||||
|
||||
import {
|
||||
|
|
@ -33,7 +34,6 @@ import { Button, type Variant, type VariantColor } from "~/components/ui/Button"
|
|||
import { Menu } from "~/components/ui/Menu"
|
||||
import { SITE_URL } from "~/lib/env"
|
||||
import { getSiteLink } from "~/lib/helpers"
|
||||
import { useTranslation } from "~/lib/i18n/client"
|
||||
import { cn } from "~/lib/utils"
|
||||
|
||||
type HeaderLinkType = {
|
||||
|
|
@ -99,7 +99,7 @@ export const ConnectButton = ({
|
|||
const { isAllRead } = useNotifications()
|
||||
|
||||
const [InsufficientBalance, setInsufficientBalance] = useState<boolean>(false)
|
||||
const { t } = useTranslation("common")
|
||||
const t = useTranslations()
|
||||
|
||||
useEffect(() => {
|
||||
if (balance) {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { useTranslation } from "~/lib/i18n/client"
|
||||
import { useTranslations } from "next-intl"
|
||||
|
||||
import { Button } from "../ui/Button"
|
||||
import { Modal } from "../ui/Modal"
|
||||
|
|
@ -14,7 +14,7 @@ export const DeleteConfirmationModal = ({
|
|||
onConfirm: () => void
|
||||
type?: string
|
||||
}) => {
|
||||
const { t } = useTranslation("common")
|
||||
const t = useTranslations()
|
||||
|
||||
return (
|
||||
<Modal open={open} setOpen={setOpen}>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { useTranslations } from "next-intl"
|
||||
import { useEffect, useRef } from "react"
|
||||
import { useTranslation } from "react-i18next"
|
||||
|
||||
import { useIsDark } from "~/hooks/useDarkMode"
|
||||
|
||||
|
|
@ -26,7 +26,7 @@ export const EmojiPicker = ({
|
|||
})
|
||||
}, [])
|
||||
|
||||
const { t } = useTranslation("common")
|
||||
const t = useTranslations()
|
||||
|
||||
return (
|
||||
<div
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
"use client"
|
||||
|
||||
import { useTranslations } from "next-intl"
|
||||
import { useState } from "react"
|
||||
|
||||
import { useAccountState } from "@crossbell/connect-kit"
|
||||
import { useRefCallback } from "@crossbell/util-hooks"
|
||||
|
||||
import { Button } from "~/components/ui/Button"
|
||||
import { useTranslation } from "~/lib/i18n/client"
|
||||
import { getSubscriptionsFromList } from "~/models/site.model"
|
||||
import { useSubscribeToSites } from "~/queries/site"
|
||||
|
||||
|
|
@ -22,7 +22,7 @@ export const FollowAllButton = ({
|
|||
className?: string
|
||||
}) => {
|
||||
const subscribeToSites = useSubscribeToSites()
|
||||
const { t } = useTranslation("index")
|
||||
const t = useTranslations()
|
||||
const account = useAccountState((s) => s.computed.account)
|
||||
const [noMore, setNoMore] = useState(false)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
"use client"
|
||||
|
||||
import { useTranslations } from "next-intl"
|
||||
import { useEffect } from "react"
|
||||
import { toast } from "react-hot-toast"
|
||||
|
||||
|
|
@ -7,7 +8,6 @@ import { Button } from "~/components/ui/Button"
|
|||
import type { Variant } from "~/components/ui/Button"
|
||||
import { UniLink } from "~/components/ui/UniLink"
|
||||
import { SITE_URL } from "~/lib/env"
|
||||
import { Trans, useTranslation } from "~/lib/i18n/client"
|
||||
import { ExpandedCharacter } from "~/lib/types"
|
||||
import { cn } from "~/lib/utils"
|
||||
import {
|
||||
|
|
@ -31,7 +31,7 @@ export const FollowingButton = ({
|
|||
}) => {
|
||||
const subscribeToSite = useSubscribeToSite()
|
||||
const unsubscribeFromSite = useUnsubscribeFromSite()
|
||||
const { t, i18n } = useTranslation("common")
|
||||
const t = useTranslations()
|
||||
|
||||
const handleClickSubscribe = () => {
|
||||
if (site?.characterId) {
|
||||
|
|
@ -62,13 +62,13 @@ export const FollowingButton = ({
|
|||
subscribeToSite.reset()
|
||||
toast.success(
|
||||
<span>
|
||||
<Trans i18n={i18n} i18nKey="Successfully followed" ns="common">
|
||||
Hey there! You're all set to{" "}
|
||||
<UniLink className="underline" href={`${SITE_URL}/`}>
|
||||
keep up with your followed blogger's latest buzz here
|
||||
</UniLink>
|
||||
.
|
||||
</Trans>
|
||||
{t.rich("Successfully followed", {
|
||||
link: (chunks) => (
|
||||
<UniLink className="underline" href={`${SITE_URL}/`}>
|
||||
{chunks}
|
||||
</UniLink>
|
||||
),
|
||||
})}
|
||||
</span>,
|
||||
{
|
||||
duration: 5000,
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
"use client"
|
||||
|
||||
import { useTranslations } from "next-intl"
|
||||
import { useState } from "react"
|
||||
|
||||
import { CharacterList } from "~/components/common/CharacterList"
|
||||
import { Button } from "~/components/ui/Button"
|
||||
import { useTranslation } from "~/lib/i18n/client"
|
||||
import {
|
||||
useGetSiteSubscriptions,
|
||||
useGetSiteToSubscriptions,
|
||||
|
|
@ -19,7 +19,7 @@ export const FollowingCount = ({
|
|||
}) => {
|
||||
let [isFollowListOpen, setIsFollowListOpen] = useState(false)
|
||||
let [isToFollowListOpen, setIsToFollowListOpen] = useState(false)
|
||||
const { t } = useTranslation("common")
|
||||
const t = useTranslations()
|
||||
|
||||
const subscriptions = useGetSiteSubscriptions({
|
||||
characterId,
|
||||
|
|
|
|||
|
|
@ -1,11 +1,20 @@
|
|||
"use client"
|
||||
|
||||
import { changeLanguage, useAvailableLanguages } from "~/lib/i18n/client"
|
||||
import { useLocale } from "next-intl"
|
||||
|
||||
import { locales } from "~/i18n"
|
||||
|
||||
import { Menu } from "../ui/Menu"
|
||||
|
||||
export function LanguageSwitch() {
|
||||
const languages = useAvailableLanguages()
|
||||
const locale = useLocale()
|
||||
|
||||
const nameMap: Record<string, string> = {
|
||||
en: "English",
|
||||
zh: "简体中文",
|
||||
"zh-TW": "繁體中文",
|
||||
ja: "日本語",
|
||||
}
|
||||
|
||||
return (
|
||||
<Menu
|
||||
|
|
@ -15,17 +24,18 @@ export function LanguageSwitch() {
|
|||
}
|
||||
dropdown={
|
||||
<>
|
||||
{languages.map((language, i) => (
|
||||
{locales.map((lo, i) => (
|
||||
<Menu.Item
|
||||
key={i}
|
||||
type="button"
|
||||
onClick={() => {
|
||||
changeLanguage(language.code)
|
||||
document.cookie = `NEXT_LOCALE=${lo};`
|
||||
window.location.reload()
|
||||
}}
|
||||
className="mx-auto"
|
||||
>
|
||||
<span>{language.name}</span>
|
||||
{language.active && (
|
||||
<span>{nameMap[lo]}</span>
|
||||
{locale === lo && (
|
||||
<span className="ml-2 icon-[mingcute--check-line]"></span>
|
||||
)}
|
||||
</Menu.Item>
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
"use client"
|
||||
|
||||
import { useTranslations } from "next-intl"
|
||||
|
||||
import { usePatronModal } from "~/components/common/PatronModal"
|
||||
import { Button } from "~/components/ui/Button"
|
||||
import { useTranslation } from "~/lib/i18n/client"
|
||||
import { ExpandedCharacter } from "~/lib/types"
|
||||
import { cn } from "~/lib/utils"
|
||||
|
||||
|
|
@ -17,7 +18,7 @@ export const PatronButton = ({
|
|||
size?: "sm" | "xl"
|
||||
loadingStatusChange?: (status: boolean) => void
|
||||
}) => {
|
||||
const { t } = useTranslation("common")
|
||||
const t = useTranslations()
|
||||
|
||||
const presentPatronModal = usePatronModal()
|
||||
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue