Skip to content

Commit

Permalink
add link validation
Browse files Browse the repository at this point in the history
  • Loading branch information
queden committed Jan 29, 2022
1 parent 4562114 commit 3184758
Show file tree
Hide file tree
Showing 7 changed files with 163 additions and 78 deletions.
29 changes: 23 additions & 6 deletions browser/src/components/ContributionCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,12 @@ import { OrbitControls } from "@react-three/drei/core/OrbitControls";
import { MdLink } from "react-icons/md";
import { getContributionLink } from "src/helpers/contributions";

import sanitizeHtml from "sanitize-html";
import parse from 'html-react-parser';
import sanitizeHtml from "sanitize-html";
import ContributionsCarousel from "./ContributionsCarousel";

interface Props {
contribution: Contribution;
contribution: ClientContribution;
hideHeader?: boolean;
isCompact?: boolean;
className?: string;
Expand All @@ -29,7 +30,7 @@ export function getFullContributionResponse({
response,
prompt,
pattern,
}: Contribution) {
}: ClientContribution) {
return (
PromptDescriptions[prompt].replace(
`{${Placeholder}}`,
Expand All @@ -46,14 +47,30 @@ export function ContributionCard({
isCompact = false,
className = "",
}: Props) {
const { author, response, prompt, pattern, createdAt, id } = contribution;
const { author, response, responseHtml, prompt, pattern, createdAt, id } = contribution;

const authorDisplay = getDisplayForAuthor(author, true);
const date = dayjs(createdAt, { utc: true });
const dateDisplay = date.format("MMM, YYYY");
const contributionLink = getContributionLink(contribution);

const responseHtml = parse(sanitizeHtml(response));

const renderHtml = (resp: string): string | JSX.Element | JSX.Element[] => {
// Remove first p tag to prevent first text going to next line, sanitize html string
// and then convert to JSX element
return parse(
sanitizeHtml(
resp.replace(
/<p[^>]*>|<\/p[^>]*>/,
""
)
)
)
};

const input = renderHtml(
responseHtml || response
);

return (
<div
Expand All @@ -74,7 +91,7 @@ export function ContributionCard({
{replaceJSX(PromptDescriptions[prompt], {
[Placeholder]: <b>{getPatternPlaceholder(pattern, prompt)}</b>,
})}{" "}
{responseHtml}
{input}
</p>
<div className={isCompact ? "blobSingleContainer" : "blobContainer"}>
{id ? (
Expand Down
10 changes: 7 additions & 3 deletions browser/src/components/ContributionSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { useCallback, useContext, useState } from "react";
import { descriptionText } from "../classNameConstants";
import {
Author,
Contribution,
ClientContribution,
Pattern,
PatternToDisplay,
Prompt,
Expand Down Expand Up @@ -40,6 +40,9 @@ import { AsyncButton } from "./core/AsyncButton";
import dayjs from "dayjs";
import { ArweaveContext } from "src/helpers/contexts/ArweaveContext";

import { Converter } from "showdown";


enum Page {
TermsOfUse,
Contribute,
Expand Down Expand Up @@ -125,7 +128,7 @@ function PreviewCard({
prompt: Prompt;
pattern: Pattern;
}) {
const contribution: Contribution = {
const contribution: ClientContribution = {
author,
response: response || "...",
prompt,
Expand Down Expand Up @@ -358,10 +361,11 @@ export function ContributionSection() {
pattern: selectedPattern,
} as any)
);
const toMarkdownConverter = new Converter();
const newContributionId = await addContribution({
prompt: selectedPrompt,
pattern: selectedPattern,
response,
response: toMarkdownConverter.makeMarkdown(response),
walletId: currentUser!.walletId,
});
// TODO: eliminate this and just return th actual contribution data with the response above.
Expand Down
4 changes: 4 additions & 0 deletions browser/src/components/core/Editor.css
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@
margin-top: 24px;
}

.invalidLink {
border-color: red;
}

.modal {
background-color: hsla(0, 0%, 15%, 1);
width:100%;
Expand Down
111 changes: 56 additions & 55 deletions browser/src/components/core/Editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ import Placeholder from "@tiptap/extension-placeholder";
import History from "@tiptap/extension-history";
import CharacterCount from "@tiptap/extension-character-count";

import { Converter } from "showdown";
import sanitizeHtml from "sanitize-html";
import isURL from "validator/lib/isURL";


import { ButtonClass } from "src/types/styles";
import { ResponseCharacterLimit } from "../ContributionSection";
import "./Editor.css";
Expand All @@ -35,20 +40,19 @@ export function Editor({
}: Props) {
const [linkInput, setLinkInput] = useState<string | null>(null);
const [displayLinkModal, setDisplayLinkModal] = useState<boolean>(false);

const sanitize = (inputHtml: string): string => {
// Remove first p tag to prevent text going to next line
return inputHtml.replace(
/<p[^>]*>|<\/p[^>]*>/,
""
);
};
const [isInvalidInput, setIsInvalidInput] = useState<boolean>(false);

const openModal = () => {
setDisplayLinkModal(true)
toggleBackgroundScrollingOnModal(false);
}

const closeModal = () => {
setDisplayLinkModal(false);
setLinkInput(null);
toggleBackgroundScrollingOnModal(true)
}

// Set Cmd/Ctrl-k shortcut
const CustomLink = Link.extend({
addKeyboardShortcuts() {
Expand Down Expand Up @@ -76,27 +80,37 @@ export function Editor({
],
onUpdate: ({ editor }) => {
onChange(
sanitize(
editor.getHTML()
)
sanitizeHtml(editor.getHTML())
);
// Okay so what we want is ContributionCard should render the
// html directly to be fast, but we should store markdown
// so we should keep onChange the same so that contribution card is
// recieve the html, but we need to find a way to persist the
// markdown, but only to the request and not the contribution card
// maybe a set markdown field? or an optional value on the AddContributionRequest
setResponseLength(editor.storage.characterCount.characters());
},
})

const setLink = (cancel: boolean) => {
if (cancel) {
// Previous url
return editor.getAttributes('link').href;
const setLink = (save: boolean) => {
setIsInvalidInput(false);
if (!save) {
closeModal();
}
var url = linkInput;

// TODO: Add Link validation

if (url === "" || url === null || url === undefined) {
//editor.chain().focus().extendMarkRange('link').unsetLink().run()
editor.chain().focus().unsetLink().run()
return
var url = null;
// If link is not null, check if it's valid and display error message otherwise.
if (!(linkInput === "" || linkInput === null || linkInput === undefined)) {
if (isURL(linkInput)) {
url = linkInput;
} else {
setIsInvalidInput(true);
return;
}
} else {
editor.chain().focus().unsetLink().run();
closeModal();
return;
}

// Add so href doesn't point to pluriverse.world/{url}
Expand All @@ -107,22 +121,9 @@ export function Editor({
url = "http://" + url;
}

// Update link
editor.chain().focus().setLink({ href: url }).run()
};

const onClickAddLink = () => {
setLink(false);
setDisplayLinkModal(false);
setLinkInput(null);
toggleBackgroundScrollingOnModal(true)
};

const onCloseModal = () => {
setLink(true);
setDisplayLinkModal(false);
setLinkInput(null);
toggleBackgroundScrollingOnModal(true);
// Set link.
editor.chain().focus().setLink({ href: url }).run();
closeModal();
};

const getPreviousLink = () => {
Expand Down Expand Up @@ -168,30 +169,30 @@ export function Editor({
}}
className={`menuItem linkIcon ${displayLinkModal ? 'shimmer' : 'white'}`}
>
<strong>🔗</strong>
<strong>🔗</strong>
</button>
</div>
</>
}
<Modal
isOpen={displayLinkModal}
<Modal
isOpen={displayLinkModal}
onAfterOpen={getPreviousLink}
className="modal"
className="modal"
overlayClassName="overlay"
onRequestClose={() => onCloseModal()}
shouldCloseOnOverlayClick={true}
>
<h3 className="text-3xl font-bold">
Add Link
Add Link
</h3>
<div>
<input
type="url"
className="linkInput"
<input
type="url"
className={`linkInput ${isInvalidInput && "invalidLink"}`}
placeholder="https://interdependence.online/declaration"
value={linkInput}
value={linkInput || ""}
onInput={e => {
setLinkInput(e.target.value)
setLinkInput((e.target.value) || "")
}}
onKeyPress={handleKeyPress}
autoFocus
Expand All @@ -201,24 +202,24 @@ export function Editor({
<div className="cancelButton">
<button
className={`${ButtonClass("blue")}`}
onClick={onCloseModal}
onClick={() => setLink(false)}
>
Cancel
Cancel
</button>
</div>
<div className="addButton">
<button
className={`${ButtonClass("blue")}`}
onClick={onClickAddLink}
onClick={() => setLink(true)}
>
Save
Save
</button>
</div>
</div>
</Modal>
<EditorContent
className="form-textarea block w-full"
editor={editor}
<EditorContent
className="form-textarea block w-full"
editor={editor}
/>
</>
)
Expand Down
27 changes: 22 additions & 5 deletions browser/src/helpers/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {
AddContributionResponse,
AddContributionRequest,
GetContributionRequest,
ClientContribution,
Contribution,
GetContributionsRequest,
AddUserRequest,
Expand All @@ -12,6 +13,8 @@ import {
VerifyTwitterRequest,
} from "../types/common/server-api";

import { Converter } from "showdown";

export function withQueryParams(
url: string,
params: Record<string, string>
Expand Down Expand Up @@ -69,23 +72,28 @@ export async function addContribution(
body: request,
method: "POST",
});
console.log(`Added ${response} contribution`);
return response as AddContributionResponse;
}

export async function getContribution({
id,
}: GetContributionRequest): Promise<Contribution> {
}: GetContributionRequest): Promise<ClientContribution> {
const response = await makeRequest(`${ApiUrl}/contributions/${id}`, {
method: "GET",
});
return response as Contribution;

const mdToHtmlConverter = new Converter();
response.responseHtml = mdToHtmlConverter.makeHtml(
response.response
);

return response as ClientContribution;
}

export async function getContributions({
offset,
contributionId,
}: GetContributionsRequest): Promise<Contribution[]> {
}: GetContributionsRequest): Promise<ClientContribution[]> {
const response = await makeRequest(
withQueryParams(`${ApiUrl}/contributions`, {
offset: offset ? String(offset) : offset,
Expand All @@ -95,7 +103,16 @@ export async function getContributions({
method: "GET",
}
);
return response as Contribution[];

const mdToHtmlConverter = new Converter();
const responseWithHtml = response.map((res) => {
res.responseHtml = mdToHtmlConverter.makeHtml(
res.response
);
return res;
});

return response as ClientContribution[];
}

export async function getUser({
Expand Down
5 changes: 5 additions & 0 deletions browser/src/types/common/server-api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,15 @@ export interface Contribution {
createdAt: Date;
}

export interface ClientContribution extends Contribution {
responseHtml?: string;
}

export interface AddContributionRequest {
walletId: string;
// This should be the full text response, formatted as markdown.
response: string;
// Full text response formatted as markdown as HTML.
prompt: Prompt;
pattern: Pattern;
}
Expand Down
Loading

0 comments on commit 3184758

Please sign in to comment.