{ const ipfs = useIpfs() + const location = useLocation() const [currentPath, setCurrentPath] = useState(undefined) const [draftArticle, setDraftArticle] = useState(INITIAL_ARTICLE_VALUE) const [article, setArticle] = useState(undefined) + const [articles, setArticles] = useState(undefined) const [executeArticleTransaction, setExecuteArticleTransaction] = useState(false) const [draftArticleThumbnail, setDraftArticleThumbnail] = useState(undefined) @@ -50,6 +53,26 @@ const ArticleProvider = ({ children }: ArticleProviderProps) => { }) const [articleHtml, setArticleHtml] = useState(undefined) + //Clear form + useEffect(() => { + const createArticleRegex = /\/new$/ + const editArticleRegex = /\/edit$/ + const previewArticleRegex = /\/preview$/ + if ( + !createArticleRegex.test(location.pathname) && + !editArticleRegex.test(location.pathname) && + !previewArticleRegex.test(location.pathname) + ) { + articleFormMethods.reset({ + title: "", + article: "", + description: "", + tags: [], + image: undefined, + }) + } + }, [location.pathname, articleFormMethods]) + const clearArticleState = () => { setCurrentPath(undefined) setDraftArticle(undefined) @@ -134,6 +157,8 @@ const ArticleProvider = ({ children }: ArticleProviderProps) => { clearArticleState, contentImageFiles, setContentImageFiles, + setArticles, + articles, }} > {children} diff --git a/packages/app/src/services/publications/contexts/article.types.ts b/packages/app/src/services/publications/contexts/article.types.ts index 1896871e..a760127c 100644 --- a/packages/app/src/services/publications/contexts/article.types.ts +++ b/packages/app/src/services/publications/contexts/article.types.ts @@ -7,6 +7,8 @@ import { articleSchema } from "@/schemas/article.schema" export type ArticleContextType = { draftArticle: Article | undefined article: Article | undefined + articles: Article[] | undefined + setArticles: (article: Article[] | undefined) => void draftArticleThumbnail: File | undefined currentPath: string | undefined markdownArticle: string | undefined @@ -46,7 +48,7 @@ export type ArticleContextType = { setContentImageFiles: (files: File[] | undefined) => void articleHtml: string | undefined setArticleHtml: React.Dispatch> - articleFormMethods: UseFormReturn>; + articleFormMethods: UseFormReturn> } export type ArticleProviderProps = { diff --git a/packages/app/src/services/publications/hooks/useArticles.ts b/packages/app/src/services/publications/hooks/useArticles.ts index ca1a9198..d43b77ce 100644 --- a/packages/app/src/services/publications/hooks/useArticles.ts +++ b/packages/app/src/services/publications/hooks/useArticles.ts @@ -1,10 +1,8 @@ -import { maxBy } from "lodash" import { useCallback, useEffect, useState } from "react" import { useQuery } from "urql" import { useNotification } from "@/hooks/useNotification" -import { Article, Publication } from "@/models/publication" -import { usePosterContext } from "@/services/poster/context" -import { INITIAL_ARTICLE_VALUE, useArticleContext } from "@/services/publications/contexts" +import { Article } from "@/models/publication" +import { useArticleContext } from "@/services/publications/contexts" import { GET_ARTICLES_QUERY, GET_ARTICLE_QUERY } from "@/services/publications/queries" import { useWalletContext } from "@/connectors/WalletProvider" import { TransactionResult, useExecuteTransaction } from "@/hooks/useContract" @@ -18,6 +16,7 @@ import { generateUpdateArticleBody, deleteArticleBody, } from "@/services/publications/utils/article-method" +import { useNavigate, useParams } from "react-router-dom" interface TransactionBody extends Object { image?: string @@ -25,14 +24,12 @@ interface TransactionBody extends Object { } const useArticles = () => { + const navigate = useNavigate() const openNotification = useNotification() - // const { transactionUrl } = usePosterContext() const { encodeIpfsHash, remotePin } = useIPFSContext() - const { saveArticle } = useArticleContext() + const { saveArticle, setArticles } = useArticleContext() + const { publicationSlug } = useParams<{ publicationSlug: string }>() const [data, setData] = useState(undefined) - // const [indexing, setIndexing] = useState(false) - // const [executePollInterval, setExecutePollInterval] = useState(false) - // const [transactionCompleted, setTransactionCompleted] = useState(false) const { signer } = useWalletContext() const [txLoading, setTxLoading] = useState({ create: false, @@ -42,6 +39,7 @@ const useArticles = () => { const [newArticleId, setNewArticleId] = useState() const [articleIdToDelete, setArticleIdToDelete] = useState("") const [articleIdToUpdate, setArticleIdToUpdate] = useState("") + const [lastUpdated, setLastUpdated] = useState(null) const { executeTransaction, status, errorMessage } = useExecuteTransaction( signer, POSTER_CONTRACT, @@ -74,6 +72,7 @@ const useArticles = () => { }, "update", "article", + lastUpdated, ) const [{ data: result, fetching: loading }, executeQuery] = useQuery({ @@ -85,36 +84,39 @@ const useArticles = () => { useEffect(() => { if (result) { setData(result.articles) + setArticles(result.articles) } else { setData(undefined) } - }, [result]) + }, [result, setArticles]) useEffect(() => { if (newArticleIndexed && newArticleId) { setTxLoading((prev) => ({ ...prev, create: false })) - console.log("newArticleId", newArticleId) - // navigate(`/${newArticleId}`) + navigate(`/${publicationSlug}/${newArticleId}`) refetch() } - }, [newArticleId, newArticleIndexed, refetch]) + }, [navigate, newArticleId, newArticleIndexed, publicationSlug, refetch]) useEffect(() => { if (articleDeletedIndexed && articleIdToDelete) { setTxLoading((prev) => ({ ...prev, delete: false })) setArticleIdToDelete("") - // navigate(`/publications`) refetch() } - }, [articleDeletedIndexed, articleIdToDelete, refetch]) + }, [articleDeletedIndexed, articleIdToDelete, refetch, setArticles]) useEffect(() => { if (articleUpdateIndexed && articleUpdateFields) { + const { article } = articleUpdateFields as { article: Article } + setTxLoading((prev) => ({ ...prev, update: false })) setArticleIdToUpdate("") - saveArticle(articleUpdateFields as Article) + setLastUpdated(null) + navigate(`/${publicationSlug}/${article.id}`) + saveArticle(article) } - }, [articleUpdateIndexed, articleUpdateFields, saveArticle]) + }, [articleUpdateIndexed, articleUpdateFields, saveArticle, navigate, publicationSlug]) const handleTransaction = async ( transactionBody: TransactionBody, @@ -122,7 +124,6 @@ const useArticles = () => { callback: (result: TransactionResult) => void, ) => { try { - console.log("transactionBody", transactionBody) setTxLoading({ ...txLoading, [transactionType]: true }) const result = await executeTransaction(JSON.stringify(transactionBody), "PUBLICATION") @@ -163,6 +164,7 @@ const useArticles = () => { const body = await generateUpdateArticleBody(publicationId, fields, encodeIpfsHash) handleTransaction({ ...body.articleBody, imgHashes: body.imgHashes }, "update", () => { setArticleIdToUpdate(fields.id) + setLastUpdated(parseInt(fields.lastUpdated ?? "")) }) } @@ -173,75 +175,9 @@ const useArticles = () => { }) } - // //Execute poll interval to know the latest publications indexed - // useEffect(() => { - // if (executePollInterval) { - // setIndexing(true) - // const interval = setInterval(() => { - // refetch() - // }, 5000) - // return () => clearInterval(interval) - // } else { - // setIndexing(false) - // } - // }, [executePollInterval, refetch]) - - // //Execute poll interval to know is the last article created is already indexed - // useEffect(() => { - // if (data && data.length && executePollInterval && draftArticle) { - // const recentArticle = maxBy(data, (fetchedArticle) => { - // if (fetchedArticle.lastUpdated) { - // return parseInt(fetchedArticle.lastUpdated) - // } - // }) - // if (recentArticle && recentArticle.title === draftArticle.title) { - // setNewArticleId(recentArticle.id) - // saveDraftArticle(INITIAL_ARTICLE_VALUE) - // saveArticle(recentArticle) - // setTransactionCompleted(true) - // setIndexing(false) - // setExecutePollInterval(false) - // openNotification({ - // message: "Execute transaction confirmed!", - // autoHideDuration: 5000, - // variant: "success", - // detailsLink: transactionUrl, - // preventDuplicate: true, - // }) - // return - // } - // } - // }, [ - // loading, - // data, - // openNotification, - // transactionUrl, - // executePollInterval, - // draftArticle, - // saveArticle, - // saveDraftArticle, - // ]) - - // //Show toast when transaction is indexing - // useEffect(() => { - // if (indexing && transactionUrl && showToast) { - // setShowToast(false) - // openNotification({ - // message: "The transaction is indexing", - // autoHideDuration: 2000, - // variant: "info", - // detailsLink: transactionUrl, - // preventDuplicate: true, - // }) - // } - // }, [indexing, openNotification, showToast, transactionUrl]) - return { loading, data, - // indexing, - // transactionCompleted, - // setExecutePollInterval, txLoading, status, errorMessage, diff --git a/packages/app/src/services/publications/utils/article-method.ts b/packages/app/src/services/publications/utils/article-method.ts index b994ed01..8d56a7df 100644 --- a/packages/app/src/services/publications/utils/article-method.ts +++ b/packages/app/src/services/publications/utils/article-method.ts @@ -18,6 +18,25 @@ export const extractAndReplaceImageHashes = (htmlString: string): { hashes: stri const modifiedHtml = new XMLSerializer().serializeToString(doc.documentElement) return { hashes, modifiedHtml } } +export const addUrlToImageHashes = (htmlString: string): string => { + + const parser = new DOMParser(); + const doc = parser.parseFromString(htmlString, 'text/html'); + + // Get all the img elements in the document + const images = doc.getElementsByTagName('img'); + + // Update the src attribute for each img element + for (let img of images) { + const src = img.getAttribute('src'); + if (src && !src.startsWith('https://ipfs.io/ipfs/')) { + img.setAttribute('src', `https://ipfs.io/ipfs/${src}`); + } + } + + // Serialize the DOM object back into a string + return new XMLSerializer().serializeToString(doc); +} const processArticleBody = async ( publicationId: string, diff --git a/packages/app/src/theme/index.ts b/packages/app/src/theme/index.ts index b14974ac..674ef4d2 100644 --- a/packages/app/src/theme/index.ts +++ b/packages/app/src/theme/index.ts @@ -401,6 +401,7 @@ let theme = createTheme({ font-family: ${typography.body1.fontFamily}; font-size: ${typography.body1.fontSize}; line-height: ${typography.body1.lineHeight}; + min-height: 28px; } .divider { height: 28px; diff --git a/packages/app/vite.config.ts b/packages/app/vite.config.ts index 83d4103c..f0a30bbf 100644 --- a/packages/app/vite.config.ts +++ b/packages/app/vite.config.ts @@ -1,6 +1,6 @@ import { defineConfig } from "vite" import react from "@vitejs/plugin-react" -import browserslistToEsbuild from "browserslist-to-esbuild" +// import browserslistToEsbuild from "browserslist-to-esbuild" import svgr from "@svgr/rollup" import path from "path" import tsconfigPaths from "vite-tsconfig-paths" @@ -14,7 +14,7 @@ export default defineConfig({ }, build: { // --> ["chrome79", "edge92", "firefox91", "safari13.1"] - target: browserslistToEsbuild(), + target: "esnext", }, resolve: { alias: [{ find: "@", replacement: path.resolve(__dirname, "src") }], diff --git a/packages/app/yarn.lock b/packages/app/yarn.lock index c0c971d3..c0af311f 100644 --- a/packages/app/yarn.lock +++ b/packages/app/yarn.lock @@ -4039,6 +4039,11 @@ resolved "https://registry.yarnpkg.com/@tiptap/extension-bold/-/extension-bold-2.3.2.tgz#ebe13406db8e7d906ad80ad7640b2e80dd6e92ce" integrity sha512-Mdc0qOPeJxxt5kSYKpNs7TzbQHeVpbpxwafUrxrvfD2iOnJlwlNxVWsVulc1t5EA8NpbTqYJTPmAtv2h/qmsfw== +"@tiptap/extension-bold@^2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@tiptap/extension-bold/-/extension-bold-2.4.0.tgz#b5ced2c3bf51f304890137dbdf394d58c01eb208" + integrity sha512-csnW6hMDEHoRfxcPRLSqeJn+j35Lgtt1YRiOwn7DlS66sAECGRuoGfCvQSPij0TCDp4VCR9if5Sf8EymhnQumQ== + "@tiptap/extension-bubble-menu@^2.3.2": version "2.3.2" resolved "https://registry.yarnpkg.com/@tiptap/extension-bubble-menu/-/extension-bubble-menu-2.3.2.tgz#58580c93b08b8953d2f9a98b35f7fd86decc252a" @@ -4066,6 +4071,11 @@ resolved "https://registry.yarnpkg.com/@tiptap/extension-code/-/extension-code-2.3.2.tgz#36689994b76550e068ca9cc29cc8721e441bf2b5" integrity sha512-LyIRBFJCxbgi96ejoeewESvfUf5igfngamZJK+uegfTcznimP0AjSWs3whJwZ9QXUsQrB9tIrWIG4GBtatp6qw== +"@tiptap/extension-code@^2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@tiptap/extension-code/-/extension-code-2.4.0.tgz#3a9fed3585bf49f445505c2e9ad71fd66e117304" + integrity sha512-wjhBukuiyJMq4cTcK3RBTzUPV24k5n1eEPlpmzku6ThwwkMdwynnMGMAmSF3fErh3AOyOUPoTTjgMYN2d10SJA== + "@tiptap/extension-color@^2.1.7": version "2.3.2" resolved "https://registry.yarnpkg.com/@tiptap/extension-color/-/extension-color-2.3.2.tgz#341a99c77fe43b67e3907194224b144e08ecae95" @@ -4076,6 +4086,11 @@ resolved "https://registry.yarnpkg.com/@tiptap/extension-document/-/extension-document-2.3.2.tgz#8914f952c946d150398913f1801295f101ded179" integrity sha512-EQcfkvA7lkZPKllhGo2jiEYLJyXhBFK7++oRatgbfgHEJ2uLBGv6ys7WLCeRA/ntcaWTH3rlS+HR/Y8/nnyQYg== +"@tiptap/extension-document@^2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@tiptap/extension-document/-/extension-document-2.4.0.tgz#a396b2cbcc8708aa2a0a41d0be481fda4b61c77b" + integrity sha512-3jRodQJZDGbXlRPERaloS+IERg/VwzpC1IO6YSJR9jVIsBO6xC29P3cKTQlg1XO7p6ZH/0ksK73VC5BzzTwoHg== + "@tiptap/extension-dropcursor@^2.3.2": version "2.3.2" resolved "https://registry.yarnpkg.com/@tiptap/extension-dropcursor/-/extension-dropcursor-2.3.2.tgz#3f12430fb5fa7e436726c66c53fe0334f97e1834" @@ -4150,6 +4165,11 @@ resolved "https://registry.yarnpkg.com/@tiptap/extension-paragraph/-/extension-paragraph-2.3.2.tgz#2141b3fcf3ca74cadf8703c07582585407de723d" integrity sha512-bKzL4NXp0pDM/Q5ZCpjLxjQU4DwoWc6CDww1M4B4dp1sfiXiE2P7EOCMM2TfJOqNPUFpp5RcFKKcxC2Suj8W4w== +"@tiptap/extension-paragraph@^2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@tiptap/extension-paragraph/-/extension-paragraph-2.4.0.tgz#5b9aea8775937b327bbe6754be12ae3144fb09ff" + integrity sha512-+yse0Ow67IRwcACd9K/CzBcxlpr9OFnmf0x9uqpaWt1eHck1sJnti6jrw5DVVkyEBHDh/cnkkV49gvctT/NyCw== + "@tiptap/extension-placeholder@2.0.3": version "2.0.3" resolved "https://registry.yarnpkg.com/@tiptap/extension-placeholder/-/extension-placeholder-2.0.3.tgz#69575353f09fc7524c9cdbfbf16c04f73c29d154" @@ -4180,11 +4200,23 @@ resolved "https://registry.yarnpkg.com/@tiptap/extension-text/-/extension-text-2.3.2.tgz#000c1c4d5ae21b8ddef777b8298b67260e13274f" integrity sha512-a3whwDyyOsrmOQbfeY+Fm5XypSRgT3IGqWgz0r4U7oko57/X6Env08F1Ie2e2UkQw9B1MoW9cm3dC6jvrdzzYA== +"@tiptap/extension-text@^2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@tiptap/extension-text/-/extension-text-2.4.0.tgz#a3a5f45a9856d513e574f24e2c9b6028273f8eb3" + integrity sha512-LV0bvE+VowE8IgLca7pM8ll7quNH+AgEHRbSrsI3SHKDCYB9gTHMjWaAkgkUVaO1u0IfCrjnCLym/PqFKa+vvg== + "@tiptap/extension-underline@^2.1.7": version "2.3.2" resolved "https://registry.yarnpkg.com/@tiptap/extension-underline/-/extension-underline-2.3.2.tgz#52ad67365c604a9afeb63600a30d13141594278a" integrity sha512-ZmhWG8gMXk62AhpIMuOofe8GWbkXBW1uYHG55Q9r7MmglESLJm13S5k8JVfOmOMKGzfE23A6yQkojnksAiSGoQ== +"@tiptap/html@^2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@tiptap/html/-/html-2.4.0.tgz#0fad860303e7f34b43d64ca8bfbf8ab260d33164" + integrity sha512-iM0sa6t0Hb5GTXnjdKvMDtD3KZgA4Mwx3QADeqfR10EjfPNlkh/BHU83oIhss/2JVRBXiUUDnNxW9cfpHX37/g== + dependencies: + zeed-dom "^0.10.9" + "@tiptap/pm@^2.1.7": version "2.3.2" resolved "https://registry.yarnpkg.com/@tiptap/pm/-/pm-2.3.2.tgz#3da84a19bf141ebfbea7b38aec7fc2bbdf8b3690" @@ -13503,6 +13535,13 @@ yup@^0.32.11: property-expr "^2.0.4" toposort "^2.0.2" +zeed-dom@^0.10.9: + version "0.10.11" + resolved "https://registry.yarnpkg.com/zeed-dom/-/zeed-dom-0.10.11.tgz#36c7ded1aa4e638794049d90d1f6b0369000c9ac" + integrity sha512-7ukbu6aQKx34OQ7PfUIxOuAhk2MvyZY/t4/IJsVzy76zuMzfhE74+Dbyp8SHiUJPTPkF0FflP1KVrGJ7gk9IHw== + dependencies: + css-what "^6.1.0" + zod-to-json-schema@3.22.5: version "3.22.5" resolved "https://registry.yarnpkg.com/zod-to-json-schema/-/zod-to-json-schema-3.22.5.tgz#3646e81cfc318dbad2a22519e5ce661615418673"
(INITIAL_ARTICLE_VALUE) const [article, setArticle] = useState(undefined) + const [articles, setArticles] = useState(undefined) const [executeArticleTransaction, setExecuteArticleTransaction] = useState(false) const [draftArticleThumbnail, setDraftArticleThumbnail] = useState(undefined) @@ -50,6 +53,26 @@ const ArticleProvider = ({ children }: ArticleProviderProps) => { }) const [articleHtml, setArticleHtml] = useState(undefined) + //Clear form + useEffect(() => { + const createArticleRegex = /\/new$/ + const editArticleRegex = /\/edit$/ + const previewArticleRegex = /\/preview$/ + if ( + !createArticleRegex.test(location.pathname) && + !editArticleRegex.test(location.pathname) && + !previewArticleRegex.test(location.pathname) + ) { + articleFormMethods.reset({ + title: "", + article: "", + description: "", + tags: [], + image: undefined, + }) + } + }, [location.pathname, articleFormMethods]) + const clearArticleState = () => { setCurrentPath(undefined) setDraftArticle(undefined) @@ -134,6 +157,8 @@ const ArticleProvider = ({ children }: ArticleProviderProps) => { clearArticleState, contentImageFiles, setContentImageFiles, + setArticles, + articles, }} > {children} diff --git a/packages/app/src/services/publications/contexts/article.types.ts b/packages/app/src/services/publications/contexts/article.types.ts index 1896871e..a760127c 100644 --- a/packages/app/src/services/publications/contexts/article.types.ts +++ b/packages/app/src/services/publications/contexts/article.types.ts @@ -7,6 +7,8 @@ import { articleSchema } from "@/schemas/article.schema" export type ArticleContextType = { draftArticle: Article | undefined article: Article | undefined + articles: Article[] | undefined + setArticles: (article: Article[] | undefined) => void draftArticleThumbnail: File | undefined currentPath: string | undefined markdownArticle: string | undefined @@ -46,7 +48,7 @@ export type ArticleContextType = { setContentImageFiles: (files: File[] | undefined) => void articleHtml: string | undefined setArticleHtml: React.Dispatch> - articleFormMethods: UseFormReturn>; + articleFormMethods: UseFormReturn> } export type ArticleProviderProps = { diff --git a/packages/app/src/services/publications/hooks/useArticles.ts b/packages/app/src/services/publications/hooks/useArticles.ts index ca1a9198..d43b77ce 100644 --- a/packages/app/src/services/publications/hooks/useArticles.ts +++ b/packages/app/src/services/publications/hooks/useArticles.ts @@ -1,10 +1,8 @@ -import { maxBy } from "lodash" import { useCallback, useEffect, useState } from "react" import { useQuery } from "urql" import { useNotification } from "@/hooks/useNotification" -import { Article, Publication } from "@/models/publication" -import { usePosterContext } from "@/services/poster/context" -import { INITIAL_ARTICLE_VALUE, useArticleContext } from "@/services/publications/contexts" +import { Article } from "@/models/publication" +import { useArticleContext } from "@/services/publications/contexts" import { GET_ARTICLES_QUERY, GET_ARTICLE_QUERY } from "@/services/publications/queries" import { useWalletContext } from "@/connectors/WalletProvider" import { TransactionResult, useExecuteTransaction } from "@/hooks/useContract" @@ -18,6 +16,7 @@ import { generateUpdateArticleBody, deleteArticleBody, } from "@/services/publications/utils/article-method" +import { useNavigate, useParams } from "react-router-dom" interface TransactionBody extends Object { image?: string @@ -25,14 +24,12 @@ interface TransactionBody extends Object { } const useArticles = () => { + const navigate = useNavigate() const openNotification = useNotification() - // const { transactionUrl } = usePosterContext() const { encodeIpfsHash, remotePin } = useIPFSContext() - const { saveArticle } = useArticleContext() + const { saveArticle, setArticles } = useArticleContext() + const { publicationSlug } = useParams<{ publicationSlug: string }>() const [data, setData] = useState(undefined) - // const [indexing, setIndexing] = useState(false) - // const [executePollInterval, setExecutePollInterval] = useState(false) - // const [transactionCompleted, setTransactionCompleted] = useState(false) const { signer } = useWalletContext() const [txLoading, setTxLoading] = useState({ create: false, @@ -42,6 +39,7 @@ const useArticles = () => { const [newArticleId, setNewArticleId] = useState() const [articleIdToDelete, setArticleIdToDelete] = useState("") const [articleIdToUpdate, setArticleIdToUpdate] = useState("") + const [lastUpdated, setLastUpdated] = useState(null) const { executeTransaction, status, errorMessage } = useExecuteTransaction( signer, POSTER_CONTRACT, @@ -74,6 +72,7 @@ const useArticles = () => { }, "update", "article", + lastUpdated, ) const [{ data: result, fetching: loading }, executeQuery] = useQuery({ @@ -85,36 +84,39 @@ const useArticles = () => { useEffect(() => { if (result) { setData(result.articles) + setArticles(result.articles) } else { setData(undefined) } - }, [result]) + }, [result, setArticles]) useEffect(() => { if (newArticleIndexed && newArticleId) { setTxLoading((prev) => ({ ...prev, create: false })) - console.log("newArticleId", newArticleId) - // navigate(`/${newArticleId}`) + navigate(`/${publicationSlug}/${newArticleId}`) refetch() } - }, [newArticleId, newArticleIndexed, refetch]) + }, [navigate, newArticleId, newArticleIndexed, publicationSlug, refetch]) useEffect(() => { if (articleDeletedIndexed && articleIdToDelete) { setTxLoading((prev) => ({ ...prev, delete: false })) setArticleIdToDelete("") - // navigate(`/publications`) refetch() } - }, [articleDeletedIndexed, articleIdToDelete, refetch]) + }, [articleDeletedIndexed, articleIdToDelete, refetch, setArticles]) useEffect(() => { if (articleUpdateIndexed && articleUpdateFields) { + const { article } = articleUpdateFields as { article: Article } + setTxLoading((prev) => ({ ...prev, update: false })) setArticleIdToUpdate("") - saveArticle(articleUpdateFields as Article) + setLastUpdated(null) + navigate(`/${publicationSlug}/${article.id}`) + saveArticle(article) } - }, [articleUpdateIndexed, articleUpdateFields, saveArticle]) + }, [articleUpdateIndexed, articleUpdateFields, saveArticle, navigate, publicationSlug]) const handleTransaction = async ( transactionBody: TransactionBody, @@ -122,7 +124,6 @@ const useArticles = () => { callback: (result: TransactionResult) => void, ) => { try { - console.log("transactionBody", transactionBody) setTxLoading({ ...txLoading, [transactionType]: true }) const result = await executeTransaction(JSON.stringify(transactionBody), "PUBLICATION") @@ -163,6 +164,7 @@ const useArticles = () => { const body = await generateUpdateArticleBody(publicationId, fields, encodeIpfsHash) handleTransaction({ ...body.articleBody, imgHashes: body.imgHashes }, "update", () => { setArticleIdToUpdate(fields.id) + setLastUpdated(parseInt(fields.lastUpdated ?? "")) }) } @@ -173,75 +175,9 @@ const useArticles = () => { }) } - // //Execute poll interval to know the latest publications indexed - // useEffect(() => { - // if (executePollInterval) { - // setIndexing(true) - // const interval = setInterval(() => { - // refetch() - // }, 5000) - // return () => clearInterval(interval) - // } else { - // setIndexing(false) - // } - // }, [executePollInterval, refetch]) - - // //Execute poll interval to know is the last article created is already indexed - // useEffect(() => { - // if (data && data.length && executePollInterval && draftArticle) { - // const recentArticle = maxBy(data, (fetchedArticle) => { - // if (fetchedArticle.lastUpdated) { - // return parseInt(fetchedArticle.lastUpdated) - // } - // }) - // if (recentArticle && recentArticle.title === draftArticle.title) { - // setNewArticleId(recentArticle.id) - // saveDraftArticle(INITIAL_ARTICLE_VALUE) - // saveArticle(recentArticle) - // setTransactionCompleted(true) - // setIndexing(false) - // setExecutePollInterval(false) - // openNotification({ - // message: "Execute transaction confirmed!", - // autoHideDuration: 5000, - // variant: "success", - // detailsLink: transactionUrl, - // preventDuplicate: true, - // }) - // return - // } - // } - // }, [ - // loading, - // data, - // openNotification, - // transactionUrl, - // executePollInterval, - // draftArticle, - // saveArticle, - // saveDraftArticle, - // ]) - - // //Show toast when transaction is indexing - // useEffect(() => { - // if (indexing && transactionUrl && showToast) { - // setShowToast(false) - // openNotification({ - // message: "The transaction is indexing", - // autoHideDuration: 2000, - // variant: "info", - // detailsLink: transactionUrl, - // preventDuplicate: true, - // }) - // } - // }, [indexing, openNotification, showToast, transactionUrl]) - return { loading, data, - // indexing, - // transactionCompleted, - // setExecutePollInterval, txLoading, status, errorMessage, diff --git a/packages/app/src/services/publications/utils/article-method.ts b/packages/app/src/services/publications/utils/article-method.ts index b994ed01..8d56a7df 100644 --- a/packages/app/src/services/publications/utils/article-method.ts +++ b/packages/app/src/services/publications/utils/article-method.ts @@ -18,6 +18,25 @@ export const extractAndReplaceImageHashes = (htmlString: string): { hashes: stri const modifiedHtml = new XMLSerializer().serializeToString(doc.documentElement) return { hashes, modifiedHtml } } +export const addUrlToImageHashes = (htmlString: string): string => { + + const parser = new DOMParser(); + const doc = parser.parseFromString(htmlString, 'text/html'); + + // Get all the img elements in the document + const images = doc.getElementsByTagName('img'); + + // Update the src attribute for each img element + for (let img of images) { + const src = img.getAttribute('src'); + if (src && !src.startsWith('https://ipfs.io/ipfs/')) { + img.setAttribute('src', `https://ipfs.io/ipfs/${src}`); + } + } + + // Serialize the DOM object back into a string + return new XMLSerializer().serializeToString(doc); +} const processArticleBody = async ( publicationId: string, diff --git a/packages/app/src/theme/index.ts b/packages/app/src/theme/index.ts index b14974ac..674ef4d2 100644 --- a/packages/app/src/theme/index.ts +++ b/packages/app/src/theme/index.ts @@ -401,6 +401,7 @@ let theme = createTheme({ font-family: ${typography.body1.fontFamily}; font-size: ${typography.body1.fontSize}; line-height: ${typography.body1.lineHeight}; + min-height: 28px; } .divider { height: 28px; diff --git a/packages/app/vite.config.ts b/packages/app/vite.config.ts index 83d4103c..f0a30bbf 100644 --- a/packages/app/vite.config.ts +++ b/packages/app/vite.config.ts @@ -1,6 +1,6 @@ import { defineConfig } from "vite" import react from "@vitejs/plugin-react" -import browserslistToEsbuild from "browserslist-to-esbuild" +// import browserslistToEsbuild from "browserslist-to-esbuild" import svgr from "@svgr/rollup" import path from "path" import tsconfigPaths from "vite-tsconfig-paths" @@ -14,7 +14,7 @@ export default defineConfig({ }, build: { // --> ["chrome79", "edge92", "firefox91", "safari13.1"] - target: browserslistToEsbuild(), + target: "esnext", }, resolve: { alias: [{ find: "@", replacement: path.resolve(__dirname, "src") }], diff --git a/packages/app/yarn.lock b/packages/app/yarn.lock index c0c971d3..c0af311f 100644 --- a/packages/app/yarn.lock +++ b/packages/app/yarn.lock @@ -4039,6 +4039,11 @@ resolved "https://registry.yarnpkg.com/@tiptap/extension-bold/-/extension-bold-2.3.2.tgz#ebe13406db8e7d906ad80ad7640b2e80dd6e92ce" integrity sha512-Mdc0qOPeJxxt5kSYKpNs7TzbQHeVpbpxwafUrxrvfD2iOnJlwlNxVWsVulc1t5EA8NpbTqYJTPmAtv2h/qmsfw== +"@tiptap/extension-bold@^2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@tiptap/extension-bold/-/extension-bold-2.4.0.tgz#b5ced2c3bf51f304890137dbdf394d58c01eb208" + integrity sha512-csnW6hMDEHoRfxcPRLSqeJn+j35Lgtt1YRiOwn7DlS66sAECGRuoGfCvQSPij0TCDp4VCR9if5Sf8EymhnQumQ== + "@tiptap/extension-bubble-menu@^2.3.2": version "2.3.2" resolved "https://registry.yarnpkg.com/@tiptap/extension-bubble-menu/-/extension-bubble-menu-2.3.2.tgz#58580c93b08b8953d2f9a98b35f7fd86decc252a" @@ -4066,6 +4071,11 @@ resolved "https://registry.yarnpkg.com/@tiptap/extension-code/-/extension-code-2.3.2.tgz#36689994b76550e068ca9cc29cc8721e441bf2b5" integrity sha512-LyIRBFJCxbgi96ejoeewESvfUf5igfngamZJK+uegfTcznimP0AjSWs3whJwZ9QXUsQrB9tIrWIG4GBtatp6qw== +"@tiptap/extension-code@^2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@tiptap/extension-code/-/extension-code-2.4.0.tgz#3a9fed3585bf49f445505c2e9ad71fd66e117304" + integrity sha512-wjhBukuiyJMq4cTcK3RBTzUPV24k5n1eEPlpmzku6ThwwkMdwynnMGMAmSF3fErh3AOyOUPoTTjgMYN2d10SJA== + "@tiptap/extension-color@^2.1.7": version "2.3.2" resolved "https://registry.yarnpkg.com/@tiptap/extension-color/-/extension-color-2.3.2.tgz#341a99c77fe43b67e3907194224b144e08ecae95" @@ -4076,6 +4086,11 @@ resolved "https://registry.yarnpkg.com/@tiptap/extension-document/-/extension-document-2.3.2.tgz#8914f952c946d150398913f1801295f101ded179" integrity sha512-EQcfkvA7lkZPKllhGo2jiEYLJyXhBFK7++oRatgbfgHEJ2uLBGv6ys7WLCeRA/ntcaWTH3rlS+HR/Y8/nnyQYg== +"@tiptap/extension-document@^2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@tiptap/extension-document/-/extension-document-2.4.0.tgz#a396b2cbcc8708aa2a0a41d0be481fda4b61c77b" + integrity sha512-3jRodQJZDGbXlRPERaloS+IERg/VwzpC1IO6YSJR9jVIsBO6xC29P3cKTQlg1XO7p6ZH/0ksK73VC5BzzTwoHg== + "@tiptap/extension-dropcursor@^2.3.2": version "2.3.2" resolved "https://registry.yarnpkg.com/@tiptap/extension-dropcursor/-/extension-dropcursor-2.3.2.tgz#3f12430fb5fa7e436726c66c53fe0334f97e1834" @@ -4150,6 +4165,11 @@ resolved "https://registry.yarnpkg.com/@tiptap/extension-paragraph/-/extension-paragraph-2.3.2.tgz#2141b3fcf3ca74cadf8703c07582585407de723d" integrity sha512-bKzL4NXp0pDM/Q5ZCpjLxjQU4DwoWc6CDww1M4B4dp1sfiXiE2P7EOCMM2TfJOqNPUFpp5RcFKKcxC2Suj8W4w== +"@tiptap/extension-paragraph@^2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@tiptap/extension-paragraph/-/extension-paragraph-2.4.0.tgz#5b9aea8775937b327bbe6754be12ae3144fb09ff" + integrity sha512-+yse0Ow67IRwcACd9K/CzBcxlpr9OFnmf0x9uqpaWt1eHck1sJnti6jrw5DVVkyEBHDh/cnkkV49gvctT/NyCw== + "@tiptap/extension-placeholder@2.0.3": version "2.0.3" resolved "https://registry.yarnpkg.com/@tiptap/extension-placeholder/-/extension-placeholder-2.0.3.tgz#69575353f09fc7524c9cdbfbf16c04f73c29d154" @@ -4180,11 +4200,23 @@ resolved "https://registry.yarnpkg.com/@tiptap/extension-text/-/extension-text-2.3.2.tgz#000c1c4d5ae21b8ddef777b8298b67260e13274f" integrity sha512-a3whwDyyOsrmOQbfeY+Fm5XypSRgT3IGqWgz0r4U7oko57/X6Env08F1Ie2e2UkQw9B1MoW9cm3dC6jvrdzzYA== +"@tiptap/extension-text@^2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@tiptap/extension-text/-/extension-text-2.4.0.tgz#a3a5f45a9856d513e574f24e2c9b6028273f8eb3" + integrity sha512-LV0bvE+VowE8IgLca7pM8ll7quNH+AgEHRbSrsI3SHKDCYB9gTHMjWaAkgkUVaO1u0IfCrjnCLym/PqFKa+vvg== + "@tiptap/extension-underline@^2.1.7": version "2.3.2" resolved "https://registry.yarnpkg.com/@tiptap/extension-underline/-/extension-underline-2.3.2.tgz#52ad67365c604a9afeb63600a30d13141594278a" integrity sha512-ZmhWG8gMXk62AhpIMuOofe8GWbkXBW1uYHG55Q9r7MmglESLJm13S5k8JVfOmOMKGzfE23A6yQkojnksAiSGoQ== +"@tiptap/html@^2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@tiptap/html/-/html-2.4.0.tgz#0fad860303e7f34b43d64ca8bfbf8ab260d33164" + integrity sha512-iM0sa6t0Hb5GTXnjdKvMDtD3KZgA4Mwx3QADeqfR10EjfPNlkh/BHU83oIhss/2JVRBXiUUDnNxW9cfpHX37/g== + dependencies: + zeed-dom "^0.10.9" + "@tiptap/pm@^2.1.7": version "2.3.2" resolved "https://registry.yarnpkg.com/@tiptap/pm/-/pm-2.3.2.tgz#3da84a19bf141ebfbea7b38aec7fc2bbdf8b3690" @@ -13503,6 +13535,13 @@ yup@^0.32.11: property-expr "^2.0.4" toposort "^2.0.2" +zeed-dom@^0.10.9: + version "0.10.11" + resolved "https://registry.yarnpkg.com/zeed-dom/-/zeed-dom-0.10.11.tgz#36c7ded1aa4e638794049d90d1f6b0369000c9ac" + integrity sha512-7ukbu6aQKx34OQ7PfUIxOuAhk2MvyZY/t4/IJsVzy76zuMzfhE74+Dbyp8SHiUJPTPkF0FflP1KVrGJ7gk9IHw== + dependencies: + css-what "^6.1.0" + zod-to-json-schema@3.22.5: version "3.22.5" resolved "https://registry.yarnpkg.com/zod-to-json-schema/-/zod-to-json-schema-3.22.5.tgz#3646e81cfc318dbad2a22519e5ce661615418673"
(undefined) + const [articles, setArticles] = useState(undefined) const [executeArticleTransaction, setExecuteArticleTransaction] = useState(false) const [draftArticleThumbnail, setDraftArticleThumbnail] = useState(undefined) @@ -50,6 +53,26 @@ const ArticleProvider = ({ children }: ArticleProviderProps) => { }) const [articleHtml, setArticleHtml] = useState(undefined) + //Clear form + useEffect(() => { + const createArticleRegex = /\/new$/ + const editArticleRegex = /\/edit$/ + const previewArticleRegex = /\/preview$/ + if ( + !createArticleRegex.test(location.pathname) && + !editArticleRegex.test(location.pathname) && + !previewArticleRegex.test(location.pathname) + ) { + articleFormMethods.reset({ + title: "", + article: "", + description: "", + tags: [], + image: undefined, + }) + } + }, [location.pathname, articleFormMethods]) + const clearArticleState = () => { setCurrentPath(undefined) setDraftArticle(undefined) @@ -134,6 +157,8 @@ const ArticleProvider = ({ children }: ArticleProviderProps) => { clearArticleState, contentImageFiles, setContentImageFiles, + setArticles, + articles, }} > {children} diff --git a/packages/app/src/services/publications/contexts/article.types.ts b/packages/app/src/services/publications/contexts/article.types.ts index 1896871e..a760127c 100644 --- a/packages/app/src/services/publications/contexts/article.types.ts +++ b/packages/app/src/services/publications/contexts/article.types.ts @@ -7,6 +7,8 @@ import { articleSchema } from "@/schemas/article.schema" export type ArticleContextType = { draftArticle: Article | undefined article: Article | undefined + articles: Article[] | undefined + setArticles: (article: Article[] | undefined) => void draftArticleThumbnail: File | undefined currentPath: string | undefined markdownArticle: string | undefined @@ -46,7 +48,7 @@ export type ArticleContextType = { setContentImageFiles: (files: File[] | undefined) => void articleHtml: string | undefined setArticleHtml: React.Dispatch> - articleFormMethods: UseFormReturn>; + articleFormMethods: UseFormReturn> } export type ArticleProviderProps = { diff --git a/packages/app/src/services/publications/hooks/useArticles.ts b/packages/app/src/services/publications/hooks/useArticles.ts index ca1a9198..d43b77ce 100644 --- a/packages/app/src/services/publications/hooks/useArticles.ts +++ b/packages/app/src/services/publications/hooks/useArticles.ts @@ -1,10 +1,8 @@ -import { maxBy } from "lodash" import { useCallback, useEffect, useState } from "react" import { useQuery } from "urql" import { useNotification } from "@/hooks/useNotification" -import { Article, Publication } from "@/models/publication" -import { usePosterContext } from "@/services/poster/context" -import { INITIAL_ARTICLE_VALUE, useArticleContext } from "@/services/publications/contexts" +import { Article } from "@/models/publication" +import { useArticleContext } from "@/services/publications/contexts" import { GET_ARTICLES_QUERY, GET_ARTICLE_QUERY } from "@/services/publications/queries" import { useWalletContext } from "@/connectors/WalletProvider" import { TransactionResult, useExecuteTransaction } from "@/hooks/useContract" @@ -18,6 +16,7 @@ import { generateUpdateArticleBody, deleteArticleBody, } from "@/services/publications/utils/article-method" +import { useNavigate, useParams } from "react-router-dom" interface TransactionBody extends Object { image?: string @@ -25,14 +24,12 @@ interface TransactionBody extends Object { } const useArticles = () => { + const navigate = useNavigate() const openNotification = useNotification() - // const { transactionUrl } = usePosterContext() const { encodeIpfsHash, remotePin } = useIPFSContext() - const { saveArticle } = useArticleContext() + const { saveArticle, setArticles } = useArticleContext() + const { publicationSlug } = useParams<{ publicationSlug: string }>() const [data, setData] = useState(undefined) - // const [indexing, setIndexing] = useState(false) - // const [executePollInterval, setExecutePollInterval] = useState(false) - // const [transactionCompleted, setTransactionCompleted] = useState(false) const { signer } = useWalletContext() const [txLoading, setTxLoading] = useState({ create: false, @@ -42,6 +39,7 @@ const useArticles = () => { const [newArticleId, setNewArticleId] = useState() const [articleIdToDelete, setArticleIdToDelete] = useState("") const [articleIdToUpdate, setArticleIdToUpdate] = useState("") + const [lastUpdated, setLastUpdated] = useState(null) const { executeTransaction, status, errorMessage } = useExecuteTransaction( signer, POSTER_CONTRACT, @@ -74,6 +72,7 @@ const useArticles = () => { }, "update", "article", + lastUpdated, ) const [{ data: result, fetching: loading }, executeQuery] = useQuery({ @@ -85,36 +84,39 @@ const useArticles = () => { useEffect(() => { if (result) { setData(result.articles) + setArticles(result.articles) } else { setData(undefined) } - }, [result]) + }, [result, setArticles]) useEffect(() => { if (newArticleIndexed && newArticleId) { setTxLoading((prev) => ({ ...prev, create: false })) - console.log("newArticleId", newArticleId) - // navigate(`/${newArticleId}`) + navigate(`/${publicationSlug}/${newArticleId}`) refetch() } - }, [newArticleId, newArticleIndexed, refetch]) + }, [navigate, newArticleId, newArticleIndexed, publicationSlug, refetch]) useEffect(() => { if (articleDeletedIndexed && articleIdToDelete) { setTxLoading((prev) => ({ ...prev, delete: false })) setArticleIdToDelete("") - // navigate(`/publications`) refetch() } - }, [articleDeletedIndexed, articleIdToDelete, refetch]) + }, [articleDeletedIndexed, articleIdToDelete, refetch, setArticles]) useEffect(() => { if (articleUpdateIndexed && articleUpdateFields) { + const { article } = articleUpdateFields as { article: Article } + setTxLoading((prev) => ({ ...prev, update: false })) setArticleIdToUpdate("") - saveArticle(articleUpdateFields as Article) + setLastUpdated(null) + navigate(`/${publicationSlug}/${article.id}`) + saveArticle(article) } - }, [articleUpdateIndexed, articleUpdateFields, saveArticle]) + }, [articleUpdateIndexed, articleUpdateFields, saveArticle, navigate, publicationSlug]) const handleTransaction = async ( transactionBody: TransactionBody, @@ -122,7 +124,6 @@ const useArticles = () => { callback: (result: TransactionResult) => void, ) => { try { - console.log("transactionBody", transactionBody) setTxLoading({ ...txLoading, [transactionType]: true }) const result = await executeTransaction(JSON.stringify(transactionBody), "PUBLICATION") @@ -163,6 +164,7 @@ const useArticles = () => { const body = await generateUpdateArticleBody(publicationId, fields, encodeIpfsHash) handleTransaction({ ...body.articleBody, imgHashes: body.imgHashes }, "update", () => { setArticleIdToUpdate(fields.id) + setLastUpdated(parseInt(fields.lastUpdated ?? "")) }) } @@ -173,75 +175,9 @@ const useArticles = () => { }) } - // //Execute poll interval to know the latest publications indexed - // useEffect(() => { - // if (executePollInterval) { - // setIndexing(true) - // const interval = setInterval(() => { - // refetch() - // }, 5000) - // return () => clearInterval(interval) - // } else { - // setIndexing(false) - // } - // }, [executePollInterval, refetch]) - - // //Execute poll interval to know is the last article created is already indexed - // useEffect(() => { - // if (data && data.length && executePollInterval && draftArticle) { - // const recentArticle = maxBy(data, (fetchedArticle) => { - // if (fetchedArticle.lastUpdated) { - // return parseInt(fetchedArticle.lastUpdated) - // } - // }) - // if (recentArticle && recentArticle.title === draftArticle.title) { - // setNewArticleId(recentArticle.id) - // saveDraftArticle(INITIAL_ARTICLE_VALUE) - // saveArticle(recentArticle) - // setTransactionCompleted(true) - // setIndexing(false) - // setExecutePollInterval(false) - // openNotification({ - // message: "Execute transaction confirmed!", - // autoHideDuration: 5000, - // variant: "success", - // detailsLink: transactionUrl, - // preventDuplicate: true, - // }) - // return - // } - // } - // }, [ - // loading, - // data, - // openNotification, - // transactionUrl, - // executePollInterval, - // draftArticle, - // saveArticle, - // saveDraftArticle, - // ]) - - // //Show toast when transaction is indexing - // useEffect(() => { - // if (indexing && transactionUrl && showToast) { - // setShowToast(false) - // openNotification({ - // message: "The transaction is indexing", - // autoHideDuration: 2000, - // variant: "info", - // detailsLink: transactionUrl, - // preventDuplicate: true, - // }) - // } - // }, [indexing, openNotification, showToast, transactionUrl]) - return { loading, data, - // indexing, - // transactionCompleted, - // setExecutePollInterval, txLoading, status, errorMessage, diff --git a/packages/app/src/services/publications/utils/article-method.ts b/packages/app/src/services/publications/utils/article-method.ts index b994ed01..8d56a7df 100644 --- a/packages/app/src/services/publications/utils/article-method.ts +++ b/packages/app/src/services/publications/utils/article-method.ts @@ -18,6 +18,25 @@ export const extractAndReplaceImageHashes = (htmlString: string): { hashes: stri const modifiedHtml = new XMLSerializer().serializeToString(doc.documentElement) return { hashes, modifiedHtml } } +export const addUrlToImageHashes = (htmlString: string): string => { + + const parser = new DOMParser(); + const doc = parser.parseFromString(htmlString, 'text/html'); + + // Get all the img elements in the document + const images = doc.getElementsByTagName('img'); + + // Update the src attribute for each img element + for (let img of images) { + const src = img.getAttribute('src'); + if (src && !src.startsWith('https://ipfs.io/ipfs/')) { + img.setAttribute('src', `https://ipfs.io/ipfs/${src}`); + } + } + + // Serialize the DOM object back into a string + return new XMLSerializer().serializeToString(doc); +} const processArticleBody = async ( publicationId: string, diff --git a/packages/app/src/theme/index.ts b/packages/app/src/theme/index.ts index b14974ac..674ef4d2 100644 --- a/packages/app/src/theme/index.ts +++ b/packages/app/src/theme/index.ts @@ -401,6 +401,7 @@ let theme = createTheme({ font-family: ${typography.body1.fontFamily}; font-size: ${typography.body1.fontSize}; line-height: ${typography.body1.lineHeight}; + min-height: 28px; } .divider { height: 28px; diff --git a/packages/app/vite.config.ts b/packages/app/vite.config.ts index 83d4103c..f0a30bbf 100644 --- a/packages/app/vite.config.ts +++ b/packages/app/vite.config.ts @@ -1,6 +1,6 @@ import { defineConfig } from "vite" import react from "@vitejs/plugin-react" -import browserslistToEsbuild from "browserslist-to-esbuild" +// import browserslistToEsbuild from "browserslist-to-esbuild" import svgr from "@svgr/rollup" import path from "path" import tsconfigPaths from "vite-tsconfig-paths" @@ -14,7 +14,7 @@ export default defineConfig({ }, build: { // --> ["chrome79", "edge92", "firefox91", "safari13.1"] - target: browserslistToEsbuild(), + target: "esnext", }, resolve: { alias: [{ find: "@", replacement: path.resolve(__dirname, "src") }], diff --git a/packages/app/yarn.lock b/packages/app/yarn.lock index c0c971d3..c0af311f 100644 --- a/packages/app/yarn.lock +++ b/packages/app/yarn.lock @@ -4039,6 +4039,11 @@ resolved "https://registry.yarnpkg.com/@tiptap/extension-bold/-/extension-bold-2.3.2.tgz#ebe13406db8e7d906ad80ad7640b2e80dd6e92ce" integrity sha512-Mdc0qOPeJxxt5kSYKpNs7TzbQHeVpbpxwafUrxrvfD2iOnJlwlNxVWsVulc1t5EA8NpbTqYJTPmAtv2h/qmsfw== +"@tiptap/extension-bold@^2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@tiptap/extension-bold/-/extension-bold-2.4.0.tgz#b5ced2c3bf51f304890137dbdf394d58c01eb208" + integrity sha512-csnW6hMDEHoRfxcPRLSqeJn+j35Lgtt1YRiOwn7DlS66sAECGRuoGfCvQSPij0TCDp4VCR9if5Sf8EymhnQumQ== + "@tiptap/extension-bubble-menu@^2.3.2": version "2.3.2" resolved "https://registry.yarnpkg.com/@tiptap/extension-bubble-menu/-/extension-bubble-menu-2.3.2.tgz#58580c93b08b8953d2f9a98b35f7fd86decc252a" @@ -4066,6 +4071,11 @@ resolved "https://registry.yarnpkg.com/@tiptap/extension-code/-/extension-code-2.3.2.tgz#36689994b76550e068ca9cc29cc8721e441bf2b5" integrity sha512-LyIRBFJCxbgi96ejoeewESvfUf5igfngamZJK+uegfTcznimP0AjSWs3whJwZ9QXUsQrB9tIrWIG4GBtatp6qw== +"@tiptap/extension-code@^2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@tiptap/extension-code/-/extension-code-2.4.0.tgz#3a9fed3585bf49f445505c2e9ad71fd66e117304" + integrity sha512-wjhBukuiyJMq4cTcK3RBTzUPV24k5n1eEPlpmzku6ThwwkMdwynnMGMAmSF3fErh3AOyOUPoTTjgMYN2d10SJA== + "@tiptap/extension-color@^2.1.7": version "2.3.2" resolved "https://registry.yarnpkg.com/@tiptap/extension-color/-/extension-color-2.3.2.tgz#341a99c77fe43b67e3907194224b144e08ecae95" @@ -4076,6 +4086,11 @@ resolved "https://registry.yarnpkg.com/@tiptap/extension-document/-/extension-document-2.3.2.tgz#8914f952c946d150398913f1801295f101ded179" integrity sha512-EQcfkvA7lkZPKllhGo2jiEYLJyXhBFK7++oRatgbfgHEJ2uLBGv6ys7WLCeRA/ntcaWTH3rlS+HR/Y8/nnyQYg== +"@tiptap/extension-document@^2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@tiptap/extension-document/-/extension-document-2.4.0.tgz#a396b2cbcc8708aa2a0a41d0be481fda4b61c77b" + integrity sha512-3jRodQJZDGbXlRPERaloS+IERg/VwzpC1IO6YSJR9jVIsBO6xC29P3cKTQlg1XO7p6ZH/0ksK73VC5BzzTwoHg== + "@tiptap/extension-dropcursor@^2.3.2": version "2.3.2" resolved "https://registry.yarnpkg.com/@tiptap/extension-dropcursor/-/extension-dropcursor-2.3.2.tgz#3f12430fb5fa7e436726c66c53fe0334f97e1834" @@ -4150,6 +4165,11 @@ resolved "https://registry.yarnpkg.com/@tiptap/extension-paragraph/-/extension-paragraph-2.3.2.tgz#2141b3fcf3ca74cadf8703c07582585407de723d" integrity sha512-bKzL4NXp0pDM/Q5ZCpjLxjQU4DwoWc6CDww1M4B4dp1sfiXiE2P7EOCMM2TfJOqNPUFpp5RcFKKcxC2Suj8W4w== +"@tiptap/extension-paragraph@^2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@tiptap/extension-paragraph/-/extension-paragraph-2.4.0.tgz#5b9aea8775937b327bbe6754be12ae3144fb09ff" + integrity sha512-+yse0Ow67IRwcACd9K/CzBcxlpr9OFnmf0x9uqpaWt1eHck1sJnti6jrw5DVVkyEBHDh/cnkkV49gvctT/NyCw== + "@tiptap/extension-placeholder@2.0.3": version "2.0.3" resolved "https://registry.yarnpkg.com/@tiptap/extension-placeholder/-/extension-placeholder-2.0.3.tgz#69575353f09fc7524c9cdbfbf16c04f73c29d154" @@ -4180,11 +4200,23 @@ resolved "https://registry.yarnpkg.com/@tiptap/extension-text/-/extension-text-2.3.2.tgz#000c1c4d5ae21b8ddef777b8298b67260e13274f" integrity sha512-a3whwDyyOsrmOQbfeY+Fm5XypSRgT3IGqWgz0r4U7oko57/X6Env08F1Ie2e2UkQw9B1MoW9cm3dC6jvrdzzYA== +"@tiptap/extension-text@^2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@tiptap/extension-text/-/extension-text-2.4.0.tgz#a3a5f45a9856d513e574f24e2c9b6028273f8eb3" + integrity sha512-LV0bvE+VowE8IgLca7pM8ll7quNH+AgEHRbSrsI3SHKDCYB9gTHMjWaAkgkUVaO1u0IfCrjnCLym/PqFKa+vvg== + "@tiptap/extension-underline@^2.1.7": version "2.3.2" resolved "https://registry.yarnpkg.com/@tiptap/extension-underline/-/extension-underline-2.3.2.tgz#52ad67365c604a9afeb63600a30d13141594278a" integrity sha512-ZmhWG8gMXk62AhpIMuOofe8GWbkXBW1uYHG55Q9r7MmglESLJm13S5k8JVfOmOMKGzfE23A6yQkojnksAiSGoQ== +"@tiptap/html@^2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@tiptap/html/-/html-2.4.0.tgz#0fad860303e7f34b43d64ca8bfbf8ab260d33164" + integrity sha512-iM0sa6t0Hb5GTXnjdKvMDtD3KZgA4Mwx3QADeqfR10EjfPNlkh/BHU83oIhss/2JVRBXiUUDnNxW9cfpHX37/g== + dependencies: + zeed-dom "^0.10.9" + "@tiptap/pm@^2.1.7": version "2.3.2" resolved "https://registry.yarnpkg.com/@tiptap/pm/-/pm-2.3.2.tgz#3da84a19bf141ebfbea7b38aec7fc2bbdf8b3690" @@ -13503,6 +13535,13 @@ yup@^0.32.11: property-expr "^2.0.4" toposort "^2.0.2" +zeed-dom@^0.10.9: + version "0.10.11" + resolved "https://registry.yarnpkg.com/zeed-dom/-/zeed-dom-0.10.11.tgz#36c7ded1aa4e638794049d90d1f6b0369000c9ac" + integrity sha512-7ukbu6aQKx34OQ7PfUIxOuAhk2MvyZY/t4/IJsVzy76zuMzfhE74+Dbyp8SHiUJPTPkF0FflP1KVrGJ7gk9IHw== + dependencies: + css-what "^6.1.0" + zod-to-json-schema@3.22.5: version "3.22.5" resolved "https://registry.yarnpkg.com/zod-to-json-schema/-/zod-to-json-schema-3.22.5.tgz#3646e81cfc318dbad2a22519e5ce661615418673"
(undefined) const [executeArticleTransaction, setExecuteArticleTransaction] = useState(false) const [draftArticleThumbnail, setDraftArticleThumbnail] = useState(undefined) @@ -50,6 +53,26 @@ const ArticleProvider = ({ children }: ArticleProviderProps) => { }) const [articleHtml, setArticleHtml] = useState(undefined) + //Clear form + useEffect(() => { + const createArticleRegex = /\/new$/ + const editArticleRegex = /\/edit$/ + const previewArticleRegex = /\/preview$/ + if ( + !createArticleRegex.test(location.pathname) && + !editArticleRegex.test(location.pathname) && + !previewArticleRegex.test(location.pathname) + ) { + articleFormMethods.reset({ + title: "", + article: "", + description: "", + tags: [], + image: undefined, + }) + } + }, [location.pathname, articleFormMethods]) + const clearArticleState = () => { setCurrentPath(undefined) setDraftArticle(undefined) @@ -134,6 +157,8 @@ const ArticleProvider = ({ children }: ArticleProviderProps) => { clearArticleState, contentImageFiles, setContentImageFiles, + setArticles, + articles, }} > {children} diff --git a/packages/app/src/services/publications/contexts/article.types.ts b/packages/app/src/services/publications/contexts/article.types.ts index 1896871e..a760127c 100644 --- a/packages/app/src/services/publications/contexts/article.types.ts +++ b/packages/app/src/services/publications/contexts/article.types.ts @@ -7,6 +7,8 @@ import { articleSchema } from "@/schemas/article.schema" export type ArticleContextType = { draftArticle: Article | undefined article: Article | undefined + articles: Article[] | undefined + setArticles: (article: Article[] | undefined) => void draftArticleThumbnail: File | undefined currentPath: string | undefined markdownArticle: string | undefined @@ -46,7 +48,7 @@ export type ArticleContextType = { setContentImageFiles: (files: File[] | undefined) => void articleHtml: string | undefined setArticleHtml: React.Dispatch> - articleFormMethods: UseFormReturn>; + articleFormMethods: UseFormReturn> } export type ArticleProviderProps = { diff --git a/packages/app/src/services/publications/hooks/useArticles.ts b/packages/app/src/services/publications/hooks/useArticles.ts index ca1a9198..d43b77ce 100644 --- a/packages/app/src/services/publications/hooks/useArticles.ts +++ b/packages/app/src/services/publications/hooks/useArticles.ts @@ -1,10 +1,8 @@ -import { maxBy } from "lodash" import { useCallback, useEffect, useState } from "react" import { useQuery } from "urql" import { useNotification } from "@/hooks/useNotification" -import { Article, Publication } from "@/models/publication" -import { usePosterContext } from "@/services/poster/context" -import { INITIAL_ARTICLE_VALUE, useArticleContext } from "@/services/publications/contexts" +import { Article } from "@/models/publication" +import { useArticleContext } from "@/services/publications/contexts" import { GET_ARTICLES_QUERY, GET_ARTICLE_QUERY } from "@/services/publications/queries" import { useWalletContext } from "@/connectors/WalletProvider" import { TransactionResult, useExecuteTransaction } from "@/hooks/useContract" @@ -18,6 +16,7 @@ import { generateUpdateArticleBody, deleteArticleBody, } from "@/services/publications/utils/article-method" +import { useNavigate, useParams } from "react-router-dom" interface TransactionBody extends Object { image?: string @@ -25,14 +24,12 @@ interface TransactionBody extends Object { } const useArticles = () => { + const navigate = useNavigate() const openNotification = useNotification() - // const { transactionUrl } = usePosterContext() const { encodeIpfsHash, remotePin } = useIPFSContext() - const { saveArticle } = useArticleContext() + const { saveArticle, setArticles } = useArticleContext() + const { publicationSlug } = useParams<{ publicationSlug: string }>() const [data, setData] = useState(undefined) - // const [indexing, setIndexing] = useState(false) - // const [executePollInterval, setExecutePollInterval] = useState(false) - // const [transactionCompleted, setTransactionCompleted] = useState(false) const { signer } = useWalletContext() const [txLoading, setTxLoading] = useState({ create: false, @@ -42,6 +39,7 @@ const useArticles = () => { const [newArticleId, setNewArticleId] = useState() const [articleIdToDelete, setArticleIdToDelete] = useState("") const [articleIdToUpdate, setArticleIdToUpdate] = useState("") + const [lastUpdated, setLastUpdated] = useState(null) const { executeTransaction, status, errorMessage } = useExecuteTransaction( signer, POSTER_CONTRACT, @@ -74,6 +72,7 @@ const useArticles = () => { }, "update", "article", + lastUpdated, ) const [{ data: result, fetching: loading }, executeQuery] = useQuery({ @@ -85,36 +84,39 @@ const useArticles = () => { useEffect(() => { if (result) { setData(result.articles) + setArticles(result.articles) } else { setData(undefined) } - }, [result]) + }, [result, setArticles]) useEffect(() => { if (newArticleIndexed && newArticleId) { setTxLoading((prev) => ({ ...prev, create: false })) - console.log("newArticleId", newArticleId) - // navigate(`/${newArticleId}`) + navigate(`/${publicationSlug}/${newArticleId}`) refetch() } - }, [newArticleId, newArticleIndexed, refetch]) + }, [navigate, newArticleId, newArticleIndexed, publicationSlug, refetch]) useEffect(() => { if (articleDeletedIndexed && articleIdToDelete) { setTxLoading((prev) => ({ ...prev, delete: false })) setArticleIdToDelete("") - // navigate(`/publications`) refetch() } - }, [articleDeletedIndexed, articleIdToDelete, refetch]) + }, [articleDeletedIndexed, articleIdToDelete, refetch, setArticles]) useEffect(() => { if (articleUpdateIndexed && articleUpdateFields) { + const { article } = articleUpdateFields as { article: Article } + setTxLoading((prev) => ({ ...prev, update: false })) setArticleIdToUpdate("") - saveArticle(articleUpdateFields as Article) + setLastUpdated(null) + navigate(`/${publicationSlug}/${article.id}`) + saveArticle(article) } - }, [articleUpdateIndexed, articleUpdateFields, saveArticle]) + }, [articleUpdateIndexed, articleUpdateFields, saveArticle, navigate, publicationSlug]) const handleTransaction = async ( transactionBody: TransactionBody, @@ -122,7 +124,6 @@ const useArticles = () => { callback: (result: TransactionResult) => void, ) => { try { - console.log("transactionBody", transactionBody) setTxLoading({ ...txLoading, [transactionType]: true }) const result = await executeTransaction(JSON.stringify(transactionBody), "PUBLICATION") @@ -163,6 +164,7 @@ const useArticles = () => { const body = await generateUpdateArticleBody(publicationId, fields, encodeIpfsHash) handleTransaction({ ...body.articleBody, imgHashes: body.imgHashes }, "update", () => { setArticleIdToUpdate(fields.id) + setLastUpdated(parseInt(fields.lastUpdated ?? "")) }) } @@ -173,75 +175,9 @@ const useArticles = () => { }) } - // //Execute poll interval to know the latest publications indexed - // useEffect(() => { - // if (executePollInterval) { - // setIndexing(true) - // const interval = setInterval(() => { - // refetch() - // }, 5000) - // return () => clearInterval(interval) - // } else { - // setIndexing(false) - // } - // }, [executePollInterval, refetch]) - - // //Execute poll interval to know is the last article created is already indexed - // useEffect(() => { - // if (data && data.length && executePollInterval && draftArticle) { - // const recentArticle = maxBy(data, (fetchedArticle) => { - // if (fetchedArticle.lastUpdated) { - // return parseInt(fetchedArticle.lastUpdated) - // } - // }) - // if (recentArticle && recentArticle.title === draftArticle.title) { - // setNewArticleId(recentArticle.id) - // saveDraftArticle(INITIAL_ARTICLE_VALUE) - // saveArticle(recentArticle) - // setTransactionCompleted(true) - // setIndexing(false) - // setExecutePollInterval(false) - // openNotification({ - // message: "Execute transaction confirmed!", - // autoHideDuration: 5000, - // variant: "success", - // detailsLink: transactionUrl, - // preventDuplicate: true, - // }) - // return - // } - // } - // }, [ - // loading, - // data, - // openNotification, - // transactionUrl, - // executePollInterval, - // draftArticle, - // saveArticle, - // saveDraftArticle, - // ]) - - // //Show toast when transaction is indexing - // useEffect(() => { - // if (indexing && transactionUrl && showToast) { - // setShowToast(false) - // openNotification({ - // message: "The transaction is indexing", - // autoHideDuration: 2000, - // variant: "info", - // detailsLink: transactionUrl, - // preventDuplicate: true, - // }) - // } - // }, [indexing, openNotification, showToast, transactionUrl]) - return { loading, data, - // indexing, - // transactionCompleted, - // setExecutePollInterval, txLoading, status, errorMessage, diff --git a/packages/app/src/services/publications/utils/article-method.ts b/packages/app/src/services/publications/utils/article-method.ts index b994ed01..8d56a7df 100644 --- a/packages/app/src/services/publications/utils/article-method.ts +++ b/packages/app/src/services/publications/utils/article-method.ts @@ -18,6 +18,25 @@ export const extractAndReplaceImageHashes = (htmlString: string): { hashes: stri const modifiedHtml = new XMLSerializer().serializeToString(doc.documentElement) return { hashes, modifiedHtml } } +export const addUrlToImageHashes = (htmlString: string): string => { + + const parser = new DOMParser(); + const doc = parser.parseFromString(htmlString, 'text/html'); + + // Get all the img elements in the document + const images = doc.getElementsByTagName('img'); + + // Update the src attribute for each img element + for (let img of images) { + const src = img.getAttribute('src'); + if (src && !src.startsWith('https://ipfs.io/ipfs/')) { + img.setAttribute('src', `https://ipfs.io/ipfs/${src}`); + } + } + + // Serialize the DOM object back into a string + return new XMLSerializer().serializeToString(doc); +} const processArticleBody = async ( publicationId: string, diff --git a/packages/app/src/theme/index.ts b/packages/app/src/theme/index.ts index b14974ac..674ef4d2 100644 --- a/packages/app/src/theme/index.ts +++ b/packages/app/src/theme/index.ts @@ -401,6 +401,7 @@ let theme = createTheme({ font-family: ${typography.body1.fontFamily}; font-size: ${typography.body1.fontSize}; line-height: ${typography.body1.lineHeight}; + min-height: 28px; } .divider { height: 28px; diff --git a/packages/app/vite.config.ts b/packages/app/vite.config.ts index 83d4103c..f0a30bbf 100644 --- a/packages/app/vite.config.ts +++ b/packages/app/vite.config.ts @@ -1,6 +1,6 @@ import { defineConfig } from "vite" import react from "@vitejs/plugin-react" -import browserslistToEsbuild from "browserslist-to-esbuild" +// import browserslistToEsbuild from "browserslist-to-esbuild" import svgr from "@svgr/rollup" import path from "path" import tsconfigPaths from "vite-tsconfig-paths" @@ -14,7 +14,7 @@ export default defineConfig({ }, build: { // --> ["chrome79", "edge92", "firefox91", "safari13.1"] - target: browserslistToEsbuild(), + target: "esnext", }, resolve: { alias: [{ find: "@", replacement: path.resolve(__dirname, "src") }], diff --git a/packages/app/yarn.lock b/packages/app/yarn.lock index c0c971d3..c0af311f 100644 --- a/packages/app/yarn.lock +++ b/packages/app/yarn.lock @@ -4039,6 +4039,11 @@ resolved "https://registry.yarnpkg.com/@tiptap/extension-bold/-/extension-bold-2.3.2.tgz#ebe13406db8e7d906ad80ad7640b2e80dd6e92ce" integrity sha512-Mdc0qOPeJxxt5kSYKpNs7TzbQHeVpbpxwafUrxrvfD2iOnJlwlNxVWsVulc1t5EA8NpbTqYJTPmAtv2h/qmsfw== +"@tiptap/extension-bold@^2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@tiptap/extension-bold/-/extension-bold-2.4.0.tgz#b5ced2c3bf51f304890137dbdf394d58c01eb208" + integrity sha512-csnW6hMDEHoRfxcPRLSqeJn+j35Lgtt1YRiOwn7DlS66sAECGRuoGfCvQSPij0TCDp4VCR9if5Sf8EymhnQumQ== + "@tiptap/extension-bubble-menu@^2.3.2": version "2.3.2" resolved "https://registry.yarnpkg.com/@tiptap/extension-bubble-menu/-/extension-bubble-menu-2.3.2.tgz#58580c93b08b8953d2f9a98b35f7fd86decc252a" @@ -4066,6 +4071,11 @@ resolved "https://registry.yarnpkg.com/@tiptap/extension-code/-/extension-code-2.3.2.tgz#36689994b76550e068ca9cc29cc8721e441bf2b5" integrity sha512-LyIRBFJCxbgi96ejoeewESvfUf5igfngamZJK+uegfTcznimP0AjSWs3whJwZ9QXUsQrB9tIrWIG4GBtatp6qw== +"@tiptap/extension-code@^2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@tiptap/extension-code/-/extension-code-2.4.0.tgz#3a9fed3585bf49f445505c2e9ad71fd66e117304" + integrity sha512-wjhBukuiyJMq4cTcK3RBTzUPV24k5n1eEPlpmzku6ThwwkMdwynnMGMAmSF3fErh3AOyOUPoTTjgMYN2d10SJA== + "@tiptap/extension-color@^2.1.7": version "2.3.2" resolved "https://registry.yarnpkg.com/@tiptap/extension-color/-/extension-color-2.3.2.tgz#341a99c77fe43b67e3907194224b144e08ecae95" @@ -4076,6 +4086,11 @@ resolved "https://registry.yarnpkg.com/@tiptap/extension-document/-/extension-document-2.3.2.tgz#8914f952c946d150398913f1801295f101ded179" integrity sha512-EQcfkvA7lkZPKllhGo2jiEYLJyXhBFK7++oRatgbfgHEJ2uLBGv6ys7WLCeRA/ntcaWTH3rlS+HR/Y8/nnyQYg== +"@tiptap/extension-document@^2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@tiptap/extension-document/-/extension-document-2.4.0.tgz#a396b2cbcc8708aa2a0a41d0be481fda4b61c77b" + integrity sha512-3jRodQJZDGbXlRPERaloS+IERg/VwzpC1IO6YSJR9jVIsBO6xC29P3cKTQlg1XO7p6ZH/0ksK73VC5BzzTwoHg== + "@tiptap/extension-dropcursor@^2.3.2": version "2.3.2" resolved "https://registry.yarnpkg.com/@tiptap/extension-dropcursor/-/extension-dropcursor-2.3.2.tgz#3f12430fb5fa7e436726c66c53fe0334f97e1834" @@ -4150,6 +4165,11 @@ resolved "https://registry.yarnpkg.com/@tiptap/extension-paragraph/-/extension-paragraph-2.3.2.tgz#2141b3fcf3ca74cadf8703c07582585407de723d" integrity sha512-bKzL4NXp0pDM/Q5ZCpjLxjQU4DwoWc6CDww1M4B4dp1sfiXiE2P7EOCMM2TfJOqNPUFpp5RcFKKcxC2Suj8W4w== +"@tiptap/extension-paragraph@^2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@tiptap/extension-paragraph/-/extension-paragraph-2.4.0.tgz#5b9aea8775937b327bbe6754be12ae3144fb09ff" + integrity sha512-+yse0Ow67IRwcACd9K/CzBcxlpr9OFnmf0x9uqpaWt1eHck1sJnti6jrw5DVVkyEBHDh/cnkkV49gvctT/NyCw== + "@tiptap/extension-placeholder@2.0.3": version "2.0.3" resolved "https://registry.yarnpkg.com/@tiptap/extension-placeholder/-/extension-placeholder-2.0.3.tgz#69575353f09fc7524c9cdbfbf16c04f73c29d154" @@ -4180,11 +4200,23 @@ resolved "https://registry.yarnpkg.com/@tiptap/extension-text/-/extension-text-2.3.2.tgz#000c1c4d5ae21b8ddef777b8298b67260e13274f" integrity sha512-a3whwDyyOsrmOQbfeY+Fm5XypSRgT3IGqWgz0r4U7oko57/X6Env08F1Ie2e2UkQw9B1MoW9cm3dC6jvrdzzYA== +"@tiptap/extension-text@^2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@tiptap/extension-text/-/extension-text-2.4.0.tgz#a3a5f45a9856d513e574f24e2c9b6028273f8eb3" + integrity sha512-LV0bvE+VowE8IgLca7pM8ll7quNH+AgEHRbSrsI3SHKDCYB9gTHMjWaAkgkUVaO1u0IfCrjnCLym/PqFKa+vvg== + "@tiptap/extension-underline@^2.1.7": version "2.3.2" resolved "https://registry.yarnpkg.com/@tiptap/extension-underline/-/extension-underline-2.3.2.tgz#52ad67365c604a9afeb63600a30d13141594278a" integrity sha512-ZmhWG8gMXk62AhpIMuOofe8GWbkXBW1uYHG55Q9r7MmglESLJm13S5k8JVfOmOMKGzfE23A6yQkojnksAiSGoQ== +"@tiptap/html@^2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@tiptap/html/-/html-2.4.0.tgz#0fad860303e7f34b43d64ca8bfbf8ab260d33164" + integrity sha512-iM0sa6t0Hb5GTXnjdKvMDtD3KZgA4Mwx3QADeqfR10EjfPNlkh/BHU83oIhss/2JVRBXiUUDnNxW9cfpHX37/g== + dependencies: + zeed-dom "^0.10.9" + "@tiptap/pm@^2.1.7": version "2.3.2" resolved "https://registry.yarnpkg.com/@tiptap/pm/-/pm-2.3.2.tgz#3da84a19bf141ebfbea7b38aec7fc2bbdf8b3690" @@ -13503,6 +13535,13 @@ yup@^0.32.11: property-expr "^2.0.4" toposort "^2.0.2" +zeed-dom@^0.10.9: + version "0.10.11" + resolved "https://registry.yarnpkg.com/zeed-dom/-/zeed-dom-0.10.11.tgz#36c7ded1aa4e638794049d90d1f6b0369000c9ac" + integrity sha512-7ukbu6aQKx34OQ7PfUIxOuAhk2MvyZY/t4/IJsVzy76zuMzfhE74+Dbyp8SHiUJPTPkF0FflP1KVrGJ7gk9IHw== + dependencies: + css-what "^6.1.0" + zod-to-json-schema@3.22.5: version "3.22.5" resolved "https://registry.yarnpkg.com/zod-to-json-schema/-/zod-to-json-schema-3.22.5.tgz#3646e81cfc318dbad2a22519e5ce661615418673"
(undefined) - // const [indexing, setIndexing] = useState(false) - // const [executePollInterval, setExecutePollInterval] = useState(false) - // const [transactionCompleted, setTransactionCompleted] = useState(false) const { signer } = useWalletContext() const [txLoading, setTxLoading] = useState({ create: false, @@ -42,6 +39,7 @@ const useArticles = () => { const [newArticleId, setNewArticleId] = useState() const [articleIdToDelete, setArticleIdToDelete] = useState("") const [articleIdToUpdate, setArticleIdToUpdate] = useState("") + const [lastUpdated, setLastUpdated] = useState(null) const { executeTransaction, status, errorMessage } = useExecuteTransaction( signer, POSTER_CONTRACT, @@ -74,6 +72,7 @@ const useArticles = () => { }, "update", "article", + lastUpdated, ) const [{ data: result, fetching: loading }, executeQuery] = useQuery({ @@ -85,36 +84,39 @@ const useArticles = () => { useEffect(() => { if (result) { setData(result.articles) + setArticles(result.articles) } else { setData(undefined) } - }, [result]) + }, [result, setArticles]) useEffect(() => { if (newArticleIndexed && newArticleId) { setTxLoading((prev) => ({ ...prev, create: false })) - console.log("newArticleId", newArticleId) - // navigate(`/${newArticleId}`) + navigate(`/${publicationSlug}/${newArticleId}`) refetch() } - }, [newArticleId, newArticleIndexed, refetch]) + }, [navigate, newArticleId, newArticleIndexed, publicationSlug, refetch]) useEffect(() => { if (articleDeletedIndexed && articleIdToDelete) { setTxLoading((prev) => ({ ...prev, delete: false })) setArticleIdToDelete("") - // navigate(`/publications`) refetch() } - }, [articleDeletedIndexed, articleIdToDelete, refetch]) + }, [articleDeletedIndexed, articleIdToDelete, refetch, setArticles]) useEffect(() => { if (articleUpdateIndexed && articleUpdateFields) { + const { article } = articleUpdateFields as { article: Article } + setTxLoading((prev) => ({ ...prev, update: false })) setArticleIdToUpdate("") - saveArticle(articleUpdateFields as Article) + setLastUpdated(null) + navigate(`/${publicationSlug}/${article.id}`) + saveArticle(article) } - }, [articleUpdateIndexed, articleUpdateFields, saveArticle]) + }, [articleUpdateIndexed, articleUpdateFields, saveArticle, navigate, publicationSlug]) const handleTransaction = async ( transactionBody: TransactionBody, @@ -122,7 +124,6 @@ const useArticles = () => { callback: (result: TransactionResult) => void, ) => { try { - console.log("transactionBody", transactionBody) setTxLoading({ ...txLoading, [transactionType]: true }) const result = await executeTransaction(JSON.stringify(transactionBody), "PUBLICATION") @@ -163,6 +164,7 @@ const useArticles = () => { const body = await generateUpdateArticleBody(publicationId, fields, encodeIpfsHash) handleTransaction({ ...body.articleBody, imgHashes: body.imgHashes }, "update", () => { setArticleIdToUpdate(fields.id) + setLastUpdated(parseInt(fields.lastUpdated ?? "")) }) } @@ -173,75 +175,9 @@ const useArticles = () => { }) } - // //Execute poll interval to know the latest publications indexed - // useEffect(() => { - // if (executePollInterval) { - // setIndexing(true) - // const interval = setInterval(() => { - // refetch() - // }, 5000) - // return () => clearInterval(interval) - // } else { - // setIndexing(false) - // } - // }, [executePollInterval, refetch]) - - // //Execute poll interval to know is the last article created is already indexed - // useEffect(() => { - // if (data && data.length && executePollInterval && draftArticle) { - // const recentArticle = maxBy(data, (fetchedArticle) => { - // if (fetchedArticle.lastUpdated) { - // return parseInt(fetchedArticle.lastUpdated) - // } - // }) - // if (recentArticle && recentArticle.title === draftArticle.title) { - // setNewArticleId(recentArticle.id) - // saveDraftArticle(INITIAL_ARTICLE_VALUE) - // saveArticle(recentArticle) - // setTransactionCompleted(true) - // setIndexing(false) - // setExecutePollInterval(false) - // openNotification({ - // message: "Execute transaction confirmed!", - // autoHideDuration: 5000, - // variant: "success", - // detailsLink: transactionUrl, - // preventDuplicate: true, - // }) - // return - // } - // } - // }, [ - // loading, - // data, - // openNotification, - // transactionUrl, - // executePollInterval, - // draftArticle, - // saveArticle, - // saveDraftArticle, - // ]) - - // //Show toast when transaction is indexing - // useEffect(() => { - // if (indexing && transactionUrl && showToast) { - // setShowToast(false) - // openNotification({ - // message: "The transaction is indexing", - // autoHideDuration: 2000, - // variant: "info", - // detailsLink: transactionUrl, - // preventDuplicate: true, - // }) - // } - // }, [indexing, openNotification, showToast, transactionUrl]) - return { loading, data, - // indexing, - // transactionCompleted, - // setExecutePollInterval, txLoading, status, errorMessage, diff --git a/packages/app/src/services/publications/utils/article-method.ts b/packages/app/src/services/publications/utils/article-method.ts index b994ed01..8d56a7df 100644 --- a/packages/app/src/services/publications/utils/article-method.ts +++ b/packages/app/src/services/publications/utils/article-method.ts @@ -18,6 +18,25 @@ export const extractAndReplaceImageHashes = (htmlString: string): { hashes: stri const modifiedHtml = new XMLSerializer().serializeToString(doc.documentElement) return { hashes, modifiedHtml } } +export const addUrlToImageHashes = (htmlString: string): string => { + + const parser = new DOMParser(); + const doc = parser.parseFromString(htmlString, 'text/html'); + + // Get all the img elements in the document + const images = doc.getElementsByTagName('img'); + + // Update the src attribute for each img element + for (let img of images) { + const src = img.getAttribute('src'); + if (src && !src.startsWith('https://ipfs.io/ipfs/')) { + img.setAttribute('src', `https://ipfs.io/ipfs/${src}`); + } + } + + // Serialize the DOM object back into a string + return new XMLSerializer().serializeToString(doc); +} const processArticleBody = async ( publicationId: string, diff --git a/packages/app/src/theme/index.ts b/packages/app/src/theme/index.ts index b14974ac..674ef4d2 100644 --- a/packages/app/src/theme/index.ts +++ b/packages/app/src/theme/index.ts @@ -401,6 +401,7 @@ let theme = createTheme({ font-family: ${typography.body1.fontFamily}; font-size: ${typography.body1.fontSize}; line-height: ${typography.body1.lineHeight}; + min-height: 28px; } .divider { height: 28px; diff --git a/packages/app/vite.config.ts b/packages/app/vite.config.ts index 83d4103c..f0a30bbf 100644 --- a/packages/app/vite.config.ts +++ b/packages/app/vite.config.ts @@ -1,6 +1,6 @@ import { defineConfig } from "vite" import react from "@vitejs/plugin-react" -import browserslistToEsbuild from "browserslist-to-esbuild" +// import browserslistToEsbuild from "browserslist-to-esbuild" import svgr from "@svgr/rollup" import path from "path" import tsconfigPaths from "vite-tsconfig-paths" @@ -14,7 +14,7 @@ export default defineConfig({ }, build: { // --> ["chrome79", "edge92", "firefox91", "safari13.1"] - target: browserslistToEsbuild(), + target: "esnext", }, resolve: { alias: [{ find: "@", replacement: path.resolve(__dirname, "src") }], diff --git a/packages/app/yarn.lock b/packages/app/yarn.lock index c0c971d3..c0af311f 100644 --- a/packages/app/yarn.lock +++ b/packages/app/yarn.lock @@ -4039,6 +4039,11 @@ resolved "https://registry.yarnpkg.com/@tiptap/extension-bold/-/extension-bold-2.3.2.tgz#ebe13406db8e7d906ad80ad7640b2e80dd6e92ce" integrity sha512-Mdc0qOPeJxxt5kSYKpNs7TzbQHeVpbpxwafUrxrvfD2iOnJlwlNxVWsVulc1t5EA8NpbTqYJTPmAtv2h/qmsfw== +"@tiptap/extension-bold@^2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@tiptap/extension-bold/-/extension-bold-2.4.0.tgz#b5ced2c3bf51f304890137dbdf394d58c01eb208" + integrity sha512-csnW6hMDEHoRfxcPRLSqeJn+j35Lgtt1YRiOwn7DlS66sAECGRuoGfCvQSPij0TCDp4VCR9if5Sf8EymhnQumQ== + "@tiptap/extension-bubble-menu@^2.3.2": version "2.3.2" resolved "https://registry.yarnpkg.com/@tiptap/extension-bubble-menu/-/extension-bubble-menu-2.3.2.tgz#58580c93b08b8953d2f9a98b35f7fd86decc252a" @@ -4066,6 +4071,11 @@ resolved "https://registry.yarnpkg.com/@tiptap/extension-code/-/extension-code-2.3.2.tgz#36689994b76550e068ca9cc29cc8721e441bf2b5" integrity sha512-LyIRBFJCxbgi96ejoeewESvfUf5igfngamZJK+uegfTcznimP0AjSWs3whJwZ9QXUsQrB9tIrWIG4GBtatp6qw== +"@tiptap/extension-code@^2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@tiptap/extension-code/-/extension-code-2.4.0.tgz#3a9fed3585bf49f445505c2e9ad71fd66e117304" + integrity sha512-wjhBukuiyJMq4cTcK3RBTzUPV24k5n1eEPlpmzku6ThwwkMdwynnMGMAmSF3fErh3AOyOUPoTTjgMYN2d10SJA== + "@tiptap/extension-color@^2.1.7": version "2.3.2" resolved "https://registry.yarnpkg.com/@tiptap/extension-color/-/extension-color-2.3.2.tgz#341a99c77fe43b67e3907194224b144e08ecae95" @@ -4076,6 +4086,11 @@ resolved "https://registry.yarnpkg.com/@tiptap/extension-document/-/extension-document-2.3.2.tgz#8914f952c946d150398913f1801295f101ded179" integrity sha512-EQcfkvA7lkZPKllhGo2jiEYLJyXhBFK7++oRatgbfgHEJ2uLBGv6ys7WLCeRA/ntcaWTH3rlS+HR/Y8/nnyQYg== +"@tiptap/extension-document@^2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@tiptap/extension-document/-/extension-document-2.4.0.tgz#a396b2cbcc8708aa2a0a41d0be481fda4b61c77b" + integrity sha512-3jRodQJZDGbXlRPERaloS+IERg/VwzpC1IO6YSJR9jVIsBO6xC29P3cKTQlg1XO7p6ZH/0ksK73VC5BzzTwoHg== + "@tiptap/extension-dropcursor@^2.3.2": version "2.3.2" resolved "https://registry.yarnpkg.com/@tiptap/extension-dropcursor/-/extension-dropcursor-2.3.2.tgz#3f12430fb5fa7e436726c66c53fe0334f97e1834" @@ -4150,6 +4165,11 @@ resolved "https://registry.yarnpkg.com/@tiptap/extension-paragraph/-/extension-paragraph-2.3.2.tgz#2141b3fcf3ca74cadf8703c07582585407de723d" integrity sha512-bKzL4NXp0pDM/Q5ZCpjLxjQU4DwoWc6CDww1M4B4dp1sfiXiE2P7EOCMM2TfJOqNPUFpp5RcFKKcxC2Suj8W4w== +"@tiptap/extension-paragraph@^2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@tiptap/extension-paragraph/-/extension-paragraph-2.4.0.tgz#5b9aea8775937b327bbe6754be12ae3144fb09ff" + integrity sha512-+yse0Ow67IRwcACd9K/CzBcxlpr9OFnmf0x9uqpaWt1eHck1sJnti6jrw5DVVkyEBHDh/cnkkV49gvctT/NyCw== + "@tiptap/extension-placeholder@2.0.3": version "2.0.3" resolved "https://registry.yarnpkg.com/@tiptap/extension-placeholder/-/extension-placeholder-2.0.3.tgz#69575353f09fc7524c9cdbfbf16c04f73c29d154" @@ -4180,11 +4200,23 @@ resolved "https://registry.yarnpkg.com/@tiptap/extension-text/-/extension-text-2.3.2.tgz#000c1c4d5ae21b8ddef777b8298b67260e13274f" integrity sha512-a3whwDyyOsrmOQbfeY+Fm5XypSRgT3IGqWgz0r4U7oko57/X6Env08F1Ie2e2UkQw9B1MoW9cm3dC6jvrdzzYA== +"@tiptap/extension-text@^2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@tiptap/extension-text/-/extension-text-2.4.0.tgz#a3a5f45a9856d513e574f24e2c9b6028273f8eb3" + integrity sha512-LV0bvE+VowE8IgLca7pM8ll7quNH+AgEHRbSrsI3SHKDCYB9gTHMjWaAkgkUVaO1u0IfCrjnCLym/PqFKa+vvg== + "@tiptap/extension-underline@^2.1.7": version "2.3.2" resolved "https://registry.yarnpkg.com/@tiptap/extension-underline/-/extension-underline-2.3.2.tgz#52ad67365c604a9afeb63600a30d13141594278a" integrity sha512-ZmhWG8gMXk62AhpIMuOofe8GWbkXBW1uYHG55Q9r7MmglESLJm13S5k8JVfOmOMKGzfE23A6yQkojnksAiSGoQ== +"@tiptap/html@^2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@tiptap/html/-/html-2.4.0.tgz#0fad860303e7f34b43d64ca8bfbf8ab260d33164" + integrity sha512-iM0sa6t0Hb5GTXnjdKvMDtD3KZgA4Mwx3QADeqfR10EjfPNlkh/BHU83oIhss/2JVRBXiUUDnNxW9cfpHX37/g== + dependencies: + zeed-dom "^0.10.9" + "@tiptap/pm@^2.1.7": version "2.3.2" resolved "https://registry.yarnpkg.com/@tiptap/pm/-/pm-2.3.2.tgz#3da84a19bf141ebfbea7b38aec7fc2bbdf8b3690" @@ -13503,6 +13535,13 @@ yup@^0.32.11: property-expr "^2.0.4" toposort "^2.0.2" +zeed-dom@^0.10.9: + version "0.10.11" + resolved "https://registry.yarnpkg.com/zeed-dom/-/zeed-dom-0.10.11.tgz#36c7ded1aa4e638794049d90d1f6b0369000c9ac" + integrity sha512-7ukbu6aQKx34OQ7PfUIxOuAhk2MvyZY/t4/IJsVzy76zuMzfhE74+Dbyp8SHiUJPTPkF0FflP1KVrGJ7gk9IHw== + dependencies: + css-what "^6.1.0" + zod-to-json-schema@3.22.5: version "3.22.5" resolved "https://registry.yarnpkg.com/zod-to-json-schema/-/zod-to-json-schema-3.22.5.tgz#3646e81cfc318dbad2a22519e5ce661615418673"