Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhance UI for User-Configured Pinning Service in Tabula #248

Merged
merged 11 commits into from
Sep 14, 2023
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,6 @@ const EditorInlineText: React.FC<InlineRichTextProps> = ({
return
}
setShowInvalidUrl(true)
console.log("The user press enter")
}

return (
Expand Down
24 changes: 18 additions & 6 deletions packages/app/src/components/commons/Editor/EditorRichText.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { DragIndicator } from "@mui/icons-material"
import { palette, typography } from "../../../theme"
import { useOnClickOutside } from "../../../hooks/useOnClickOutside"
import { useArticleContext } from "../../../services/publications/contexts"
import useLocalStorage from "../../../hooks/useLocalStorage"

const RichTextButton = styled(Box)({
position: "relative",
Expand Down Expand Up @@ -75,6 +76,7 @@ type RichTextItemProps = {
color?: string
icon: React.ReactNode
selected?: boolean
disabled?: boolean
}

type RichTextProps = {
Expand Down Expand Up @@ -190,7 +192,7 @@ const DragTooltipContent = () => {
)
}

const RichTextItem: React.FC<RichTextItemProps> = ({ label, icon, color, selected }) => {
const RichTextItem: React.FC<RichTextItemProps> = ({ label, icon, color, selected, disabled }) => {
return (
<Grid
container
Expand All @@ -199,7 +201,10 @@ const RichTextItem: React.FC<RichTextItemProps> = ({ label, icon, color, selecte
alignItems="center"
sx={{
cursor: "pointer",
"& .rich-text-icon": { backgroundColor: selected ? palette.grays[100] : palette.grays[50] },
"& .rich-text-icon": {
backgroundColor: selected ? palette.grays[100] : palette.grays[50],
opacity: disabled ? 0.5 : "initial",
},
"&:hover": {
"& .rich-text-icon": { backgroundColor: palette.grays[100] },
},
Expand All @@ -211,7 +216,7 @@ const RichTextItem: React.FC<RichTextItemProps> = ({ label, icon, color, selecte
{label && (
<Grid item>
<Typography
color={color}
color={disabled ? palette.grays[400] : color}
variant="body2"
fontWeight={600}
fontSize={11}
Expand All @@ -227,7 +232,7 @@ const RichTextItem: React.FC<RichTextItemProps> = ({ label, icon, color, selecte

const EditorRichText: React.FC<RichTextProps> = ({ onRichTextSelected, showCommand, onDelete, onAdd }) => {
const { setShowBlockTypePopup } = useArticleContext()

const [pinning] = useLocalStorage("pinning", undefined)
const containerRef = useRef<Element | (() => Element | null) | null>(null)
const richTextRef = useRef<HTMLDivElement | null>(null)
const ref = useRef<HTMLDivElement | null>(null)
Expand Down Expand Up @@ -283,7 +288,6 @@ const EditorRichText: React.FC<RichTextProps> = ({ onRichTextSelected, showComma
case "Enter":
event.preventDefault()
setSelectedIndex((currentIndex) => {
console.log("currentIndex", currentIndex)
const selectedOption = richTextOption[currentIndex]
if (selectedOption) {
handleSelection(selectedOption.value)
Expand Down Expand Up @@ -348,6 +352,9 @@ const EditorRichText: React.FC<RichTextProps> = ({ onRichTextSelected, showComma
}, [topOffset])

const handleSelection = (value: RICH_TEXT_ELEMENTS) => {
if (value === RICH_TEXT_ELEMENTS.IMAGE && !pinning) {
return
}
if (onRichTextSelected) {
onRichTextSelected(value)
setShow(false)
Expand Down Expand Up @@ -423,7 +430,12 @@ const EditorRichText: React.FC<RichTextProps> = ({ onRichTextSelected, showComma
{OPTIONS.map(({ label, icon, value }, index) => (
<Grid item key={`${label}-${index}`} ref={optionRefs[index]}>
<div onClick={() => value && handleSelection(value)} tabIndex={0}>
<RichTextItem label={label} icon={icon} selected={index + 6 === selectedIndex} />
<RichTextItem
label={label}
icon={icon}
disabled={!pinning && value === RICH_TEXT_ELEMENTS.IMAGE ? true : false}
selected={index + HEADER_OPTIONS.length === selectedIndex}
/>
</div>
</Grid>
))}
Expand Down
26 changes: 23 additions & 3 deletions packages/app/src/components/commons/PinningAlert.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,11 @@ const StyledButton = styled(Button)({
},
})

export const PinningAlert: React.FC = () => {
type PinningAlertProps = {
secondMsg?: boolean
}

export const PinningAlert: React.FC<PinningAlertProps> = ({ secondMsg }) => {
const navigate = useNavigate()
const location = useLocation()
const { setCurrentPath } = usePublicationContext()
Expand All @@ -43,8 +47,24 @@ export const PinningAlert: React.FC = () => {

<Grid item>
<Typography variant="body1" fontWeight={500} color={palette.secondary[1000]}>
It is not recommended to publish an article without a configured pinning service. Without a configured
pinning service, your transactions will be much more expensive.
{secondMsg ? (
<>
<Box>
<span style={{ fontWeight: "bold" }}> Use the Public IPFS-Gateway:</span> Retrieve the hash content,
but note that we don't store images or articles.
</Box>
<Box>
<span style={{ fontWeight: "bold" }}>Set Up a Custom Pinning Service:</span> Trust your preferred
service for more personalized control.
</Box>
<Box>
<span style={{ fontWeight: "bold" }}> Post the Article Directly on Chain: </span> Keep in mind, this
is more expensive, and images are not supported.
</Box>
</>
) : (
"It is not recommended to publish an article without a configured pinning service. Without a configured pinning service, your transactions will be much more expensive."
)}
</Typography>
</Grid>
<Grid item>
Expand Down
1 change: 0 additions & 1 deletion packages/app/src/components/commons/PublicationAvatar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,6 @@ const PublicationAvatar: React.FC<PublicationAvatarProps> = ({ defaultImage, onF
}

const handleShowImg = (): string | undefined => {
console.log('uri', uri)
if (uri) {
return uri
}
Expand Down
18 changes: 15 additions & 3 deletions packages/app/src/components/commons/UploadFile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,16 @@ type UploadFileProps = {
defaultUri?: string | undefined
onFileSelected: (file: File | undefined) => void
convertedFile?: (uri: string | undefined) => void
disabled?: boolean
}

export const UploadFile: React.FC<UploadFileProps> = ({ defaultImage, defaultUri, onFileSelected, convertedFile }) => {
export const UploadFile: React.FC<UploadFileProps> = ({
defaultImage,
defaultUri,
onFileSelected,
convertedFile,
disabled,
}) => {
const inputFile = useRef<HTMLInputElement | null>(null)
const [file, setFile] = useState<File | null>(null)
const [uri, setUri] = useState<string | null>(null)
Expand Down Expand Up @@ -71,7 +78,7 @@ export const UploadFile: React.FC<UploadFileProps> = ({ defaultImage, defaultUri
}
}, [defaultImage, ipfs, defaultImageSrc])

const openImagePicker = () => inputFile && inputFile.current?.click()
const openImagePicker = () => !disabled && inputFile && inputFile.current?.click()

const removeImage = () => {
setFile(null)
Expand All @@ -94,7 +101,12 @@ export const UploadFile: React.FC<UploadFileProps> = ({ defaultImage, defaultUri
return (
<>
{!imageHash && !uri && (
<UploadFileContainer container gap={1} onClick={openImagePicker}>
<UploadFileContainer
container
gap={1}
onClick={openImagePicker}
sx={{ background: disabled ? palette.grays[200] : palette.grays[50] }}
>
<AddIcon />
<Typography fontSize={14} textAlign="center" color={palette.grays[600]} lineHeight={1.5} maxWidth="80%">
Include a high-quality image in your post to make it more inviting.
Expand Down
3 changes: 2 additions & 1 deletion packages/app/src/components/layout/ArticleHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ const ArticleHeader: React.FC<Props> = ({ publication, type }) => {
let hashArticle
const { title, article: draftArticleText, description, tags } = article

if (draftArticleThumbnail) {
if (pinning && draftArticleThumbnail) {
await ipfs.uploadContent(draftArticleThumbnail).then(async (img) => {
articleThumbnail = img.path
})
Expand All @@ -246,6 +246,7 @@ const ArticleHeader: React.FC<Props> = ({ publication, type }) => {
if (title) {
if (type === "new") {
console.log("before start")

return await createArticle(
{
action: "article/create",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,15 @@ import CreateArticlePage from "../../layout/CreateArticlePage"
import { ArticleContentSection } from "./components/ArticleContentSection"
import { palette } from "../../../theme"
import useDebouncedState from "../../../hooks/useDebouncedState"
import useLocalStorage from "../../../hooks/useLocalStorage"
import { PinningAlert } from "../../commons/PinningAlert"

interface CreateArticleViewProps {
type: "new" | "edit"
}

export const CreateArticleView: React.FC<CreateArticleViewProps> = React.memo(({ type }) => {
const [pinning] = useLocalStorage("pinning", undefined)
const { publication } = usePublicationContext()
const { draftArticle, updateDraftArticle, articleTitleError, articleContentError } = useArticleContext()

Expand All @@ -32,6 +35,11 @@ export const CreateArticleView: React.FC<CreateArticleViewProps> = React.memo(({
>
<Container maxWidth="md" sx={{ px: [8] }}>
<Grid container gap={4} flexDirection="column" my={12.5}>
{!pinning && (
<Grid item xs={12}>
<PinningAlert secondMsg />
</Grid>
)}
<Grid item xs={12}>
<Stack spacing={1}>
<InputLabel>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* eslint-disable react-hooks/exhaustive-deps */
import React, { useState, SetStateAction, useEffect, useCallback } from "react"
import { Box, InputLabel, Stack, TextField, Typography, useTheme } from "@mui/material"
import { Box, InputLabel, Stack, TextField, Tooltip, Typography, useTheme } from "@mui/material"
import { useArticleContext } from "../../../../services/publications/contexts"
import { Close } from "@mui/icons-material"
import { palette, typography } from "../../../../theme"
Expand All @@ -9,13 +9,15 @@ import { UploadFile } from "../../../commons/UploadFile"
import { CreatableSelect } from "../../../commons/CreatableSelect"
import { CreateSelectOption } from "../../../../models/dropdown"
import useDebouncedState from "../../../../hooks/useDebouncedState"
import useLocalStorage from "../../../../hooks/useLocalStorage"

export interface ArticleSidebarProps {
showSidebar: boolean
setShowSidebar: React.Dispatch<SetStateAction<boolean>>
}

const ArticleSidebar: React.FC<ArticleSidebarProps> = ({ showSidebar, setShowSidebar }) => {
const [pinning] = useLocalStorage("pinning", undefined)
const { article } = useArticleContext()
const { draftArticle, saveDraftArticle, setDraftArticleThumbnail, draftArticleThumbnail, updateDraftArticle } =
useArticleContext()
Expand Down Expand Up @@ -143,15 +145,18 @@ const ArticleSidebar: React.FC<ArticleSidebarProps> = ({ showSidebar, setShowSid
}}
>
{/* Thumbnail */}
<Stack spacing={1}>
<InputLabel>Thumbnail</InputLabel>
<UploadFile
defaultImage={article?.image}
defaultUri={draftArticle?.image ?? undefined}
onFileSelected={handleOnFiles}
convertedFile={setUriImage}
/>
</Stack>
<Tooltip title={!pinning ? "If you'd like to include images, you need to configure a pinning service." : ""}>
<Stack spacing={1}>
<InputLabel>Thumbnail</InputLabel>
<UploadFile
defaultImage={article?.image}
defaultUri={draftArticle?.image ?? undefined}
onFileSelected={handleOnFiles}
convertedFile={setUriImage}
disabled={!pinning}
/>
</Stack>
</Tooltip>

{/* Post URL */}
{/* <Stack spacing={1}>
Expand Down
2 changes: 0 additions & 2 deletions packages/app/src/services/turndown.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,9 @@ turndownService.addRule("pre", {
turndownService.addRule("figure", {
filter: function (node) {
const isMatch = node.nodeName === "FIGURE" && node.innerHTML.trim() === "&nbsp;"
// console.log(`Checking node: ${node.outerHTML}, isMatch: ${isMatch}`)
return isMatch
},
replacement: function () {
// console.log("Replacing figure node with divider")
return "\n\n---\n\n"
},
})
Expand Down
Loading