diff --git a/browser/src/components/ContributionCard.tsx b/browser/src/components/ContributionCard.tsx
index 7922d24..6f95a8a 100644
--- a/browser/src/components/ContributionCard.tsx
+++ b/browser/src/components/ContributionCard.tsx
@@ -1,6 +1,6 @@
import {
Author,
- Contribution,
+ ClientContribution,
PatternToDisplay,
} from "src/types/common/server-api";
import dayjs from "dayjs";
@@ -14,11 +14,12 @@ import {
import { getPatternPlaceholder } from "src/types";
import { getDisplayForAuthor } from "./SignatureContent";
-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;
@@ -28,7 +29,7 @@ export function getFullContributionResponse({
response,
prompt,
pattern,
-}: Contribution) {
+}: ClientContribution) {
return (
PromptDescriptions[prompt].replace(
`{${Placeholder}}`,
@@ -45,13 +46,28 @@ export function ContributionCard({
isCompact = false,
className = "",
}: Props) {
- const { author, response, prompt, pattern, createdAt } = contribution;
+ const { author, response, responseHtml, prompt, pattern, createdAt } = contribution;
const authorDisplay = getDisplayForAuthor(author, true);
const date = dayjs(createdAt, { utc: true });
const dateDisplay = date.format("MMM, YYYY");
-
- 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[^>]*>/,
+ ""
+ )
+ )
+ )
+ };
+
+ const input = renderHtml(
+ responseHtml || response
+ );
return (
{getPatternPlaceholder(pattern, prompt)},
})}{" "}
- {responseHtml}
+ {input}
(null);
const [displayLinkModal, setDisplayLinkModal] = useState(false);
-
- const sanitize = (inputHtml: string): string => {
- // Remove first p tag to prevent text going to next line
- return inputHtml.replace(
- /]*>|<\/p[^>]*>/,
- ""
- );
- };
+ const [isInvalidInput, setIsInvalidInput] = useState(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() {
@@ -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}
@@ -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 = () => {
@@ -168,30 +169,30 @@ export function Editor({
}}
className={`menuItem linkIcon ${displayLinkModal ? 'shimmer' : 'white'}`}
>
- 🔗
+ 🔗
>
}
-
onCloseModal()}
shouldCloseOnOverlayClick={true}
>
- Add Link
+ Add Link
-
{
- setLinkInput(e.target.value)
+ setLinkInput((e.target.value) || "")
}}
onKeyPress={handleKeyPress}
autoFocus
@@ -201,24 +202,24 @@ export function Editor({
setLink(false)}
>
- Cancel
+ Cancel
setLink(true)}
>
- Save
+ Save
-
>
)
diff --git a/browser/src/helpers/api.ts b/browser/src/helpers/api.ts
index 6056684..952abfe 100644
--- a/browser/src/helpers/api.ts
+++ b/browser/src/helpers/api.ts
@@ -3,6 +3,7 @@ import {
AddContributionResponse,
AddContributionRequest,
GetContributionRequest,
+ ClientContribution,
Contribution,
GetContributionsRequest,
AddUserRequest,
@@ -13,6 +14,8 @@ import {
VerifyTwitterRequest,
} from "../types/common/server-api";
+import { Converter } from "showdown";
+
export function withQueryParams(
url: string,
params: Record
@@ -70,23 +73,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 {
+}: GetContributionRequest): Promise {
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 {
+}: GetContributionsRequest): Promise {
const response = await makeRequest(
withQueryParams(`${ApiUrl}/contributions`, {
offset: offset ? String(offset) : offset,
@@ -96,7 +104,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({
diff --git a/browser/src/types/common/server-api/index.ts b/browser/src/types/common/server-api/index.ts
index 01872e4..e3a7b97 100644
--- a/browser/src/types/common/server-api/index.ts
+++ b/browser/src/types/common/server-api/index.ts
@@ -59,10 +59,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;
}
diff --git a/browser/yarn.lock b/browser/yarn.lock
index 054837c..99c6dc0 100644
--- a/browser/yarn.lock
+++ b/browser/yarn.lock
@@ -5566,6 +5566,11 @@ entities@^3.0.1:
resolved "https://registry.yarnpkg.com/entities/-/entities-3.0.1.tgz#2b887ca62585e96db3903482d336c1006c3001d4"
integrity sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==
+entities@~2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/entities/-/entities-2.1.0.tgz#992d3129cf7df6870b96c57858c249a120f8b8b5"
+ integrity sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==
+
err-code@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/err-code/-/err-code-3.0.1.tgz#a444c7b992705f2b120ee320b09972eef331c920"
@@ -7154,7 +7159,7 @@ hash.js@1.1.7, hash.js@^1.0.0, hash.js@^1.0.3, hash.js@^1.1.7:
inherits "^2.0.3"
minimalistic-assert "^1.0.1"
-he@1.2.0, he@^1.2.0:
+he@^1.2.0:
version "1.2.0"
resolved "https://registry.npmjs.org/he/-/he-1.2.0.tgz"
integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
@@ -8918,6 +8923,13 @@ lines-and-columns@^1.1.6:
resolved "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz"
integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==
+linkify-it@^3.0.1:
+ version "3.0.3"
+ resolved "https://registry.yarnpkg.com/linkify-it/-/linkify-it-3.0.3.tgz#a98baf44ce45a550efb4d49c769d07524cc2fa2e"
+ integrity sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==
+ dependencies:
+ uc.micro "^1.0.1"
+
linkifyjs@^3.0.5:
version "3.0.5"
resolved "https://registry.yarnpkg.com/linkifyjs/-/linkifyjs-3.0.5.tgz#99e51a3a0c0e232fcb63ebb89eea3ff923378f34"
@@ -9106,6 +9118,17 @@ map-visit@^1.0.0:
dependencies:
object-visit "^1.0.0"
+markdown-it@^12.0.0:
+ version "12.3.2"
+ resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-12.3.2.tgz#bf92ac92283fe983fe4de8ff8abfb5ad72cd0c90"
+ integrity sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==
+ dependencies:
+ argparse "^2.0.1"
+ entities "~2.1.0"
+ linkify-it "^3.0.1"
+ mdurl "^1.0.1"
+ uc.micro "^1.0.5"
+
md5.js@^1.3.4:
version "1.3.5"
resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f"
@@ -9125,6 +9148,11 @@ mdn-data@2.0.4:
resolved "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz"
integrity sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==
+mdurl@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e"
+ integrity sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=
+
media-typer@0.3.0:
version "0.3.0"
resolved "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz"
@@ -9466,14 +9494,6 @@ node-gyp-build@^4.2.0:
resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.3.0.tgz#9f256b03e5826150be39c764bf51e993946d71a3"
integrity sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q==
-node-html-parser@^5.2.0:
- version "5.2.0"
- resolved "https://registry.yarnpkg.com/node-html-parser/-/node-html-parser-5.2.0.tgz#6f29fd00d79f65334e7e20200964644207925607"
- integrity sha512-fmiwLfQu+J2A0zjwSEkztSHexAf5qq/WoiL/Hgo1K7JpfEP+OGWY5maG0kGaM+IFVdixF/1QbyXaQ3h4cGfeLw==
- dependencies:
- css-select "^4.1.3"
- he "1.2.0"
-
node-int64@^0.4.0:
version "0.4.0"
resolved "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz"
@@ -11973,6 +11993,13 @@ shell-quote@^1.7.3:
resolved "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.3.tgz"
integrity sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw==
+showdown@^1.9.1:
+ version "1.9.1"
+ resolved "https://registry.yarnpkg.com/showdown/-/showdown-1.9.1.tgz#134e148e75cd4623e09c21b0511977d79b5ad0ef"
+ integrity sha512-9cGuS382HcvExtf5AHk7Cb4pAeQQ+h0eTr33V1mu+crYWV4KvWAw6el92bDrqGEk5d46Ai/fhbEUwqJ/mTCNEA==
+ dependencies:
+ yargs "^14.2"
+
side-channel@^1.0.4:
version "1.0.4"
resolved "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz"
@@ -12966,6 +12993,11 @@ typescript@^4.4.2:
resolved "https://registry.npmjs.org/typescript/-/typescript-4.5.4.tgz"
integrity sha512-VgYs2A2QIRuGphtzFV7aQJduJ2gyfTljngLzjpfW9FoYZF6xuw1W0vW9ghCKLfcWrCFxK81CSGRAvS1pn4fIUg==
+uc.micro@^1.0.1, uc.micro@^1.0.5:
+ version "1.0.6"
+ resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac"
+ integrity sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==
+
uint8arrays@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/uint8arrays/-/uint8arrays-3.0.0.tgz#260869efb8422418b6f04e3fac73a3908175c63b"
@@ -13162,6 +13194,11 @@ v8-to-istanbul@^8.1.0:
convert-source-map "^1.6.0"
source-map "^0.7.3"
+validator@^13.7.0:
+ version "13.7.0"
+ resolved "https://registry.yarnpkg.com/validator/-/validator-13.7.0.tgz#4f9658ba13ba8f3d82ee881d3516489ea85c0857"
+ integrity sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw==
+
varint@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/varint/-/varint-6.0.0.tgz#9881eb0ce8feaea6512439d19ddf84bf551661d0"
@@ -13839,6 +13876,14 @@ yargs-parser@^13.1.2:
camelcase "^5.0.0"
decamelize "^1.2.0"
+yargs-parser@^15.0.1:
+ version "15.0.3"
+ resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-15.0.3.tgz#316e263d5febe8b38eef61ac092b33dfcc9b1115"
+ integrity sha512-/MVEVjTXy/cGAjdtQf8dW3V9b97bPN7rNn8ETj6BmAQL7ibC7O1Q9SPJbGjgh3SlwoBNXMzj/ZGIj8mBgl12YA==
+ dependencies:
+ camelcase "^5.0.0"
+ decamelize "^1.2.0"
+
yargs-parser@^20.2.2:
version "20.2.9"
resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz"
@@ -13860,6 +13905,23 @@ yargs@^13.2.4:
y18n "^4.0.0"
yargs-parser "^13.1.2"
+yargs@^14.2:
+ version "14.2.3"
+ resolved "https://registry.yarnpkg.com/yargs/-/yargs-14.2.3.tgz#1a1c3edced1afb2a2fea33604bc6d1d8d688a414"
+ integrity sha512-ZbotRWhF+lkjijC/VhmOT9wSgyBQ7+zr13+YLkhfsSiTriYsMzkTUFP18pFhWwBeMa5gUc1MzbhrO6/VB7c9Xg==
+ dependencies:
+ cliui "^5.0.0"
+ decamelize "^1.2.0"
+ find-up "^3.0.0"
+ get-caller-file "^2.0.1"
+ require-directory "^2.1.1"
+ require-main-filename "^2.0.0"
+ set-blocking "^2.0.0"
+ string-width "^3.0.0"
+ which-module "^2.0.0"
+ y18n "^4.0.0"
+ yargs-parser "^15.0.1"
+
yargs@^16.2.0:
version "16.2.0"
resolved "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz"