mirror of
https://github.com/BlowaterNostr/blowater.git
synced 2024-10-18 07:33:22 +00:00
better url parsing (#349)
This commit is contained in:
parent
f72621a167
commit
a9afd8291d
@ -1,5 +1,5 @@
|
||||
import { assertEquals } from "https://deno.land/std@0.176.0/testing/asserts.ts";
|
||||
import { ChatMessage, groupContinuousMessages, parseContent } from "./message.ts";
|
||||
import { ChatMessage, findUrlInString, groupContinuousMessages, parseContent } from "./message.ts";
|
||||
import { PrivateKey, PublicKey } from "../lib/nostr-ts/key.ts";
|
||||
import { Nevent, NostrAddress } from "../lib/nostr-ts/nip19.ts";
|
||||
import { NostrKind } from "../lib/nostr-ts/nostr.ts";
|
||||
@ -287,3 +287,24 @@ Deno.test("if there is no message, should not yield any group", () => {
|
||||
assertEquals(group.value, undefined);
|
||||
assertEquals(group.done, true);
|
||||
});
|
||||
|
||||
Deno.test("findUrlInString should include non-URL parts", () => {
|
||||
const result = findUrlInString("Visit http://example.com for more info.");
|
||||
assertEquals(result, ["Visit ", new URL("http://example.com"), " for more info."]);
|
||||
});
|
||||
|
||||
Deno.test("findUrlInString with multiple URLs and text parts", () => {
|
||||
const result = findUrlInString("Go to http://example.com and https://example.org for info.");
|
||||
assertEquals(result, [
|
||||
"Go to ",
|
||||
new URL("http://example.com"),
|
||||
" and ",
|
||||
new URL("https://example.org"),
|
||||
" for info.",
|
||||
]);
|
||||
});
|
||||
|
||||
Deno.test("findUrlInString with only text", () => {
|
||||
const result = findUrlInString("No URLs here.");
|
||||
assertEquals(result, ["No URLs here."]);
|
||||
});
|
||||
|
@ -227,3 +227,32 @@ export function sortMessage(messages: ChatMessage[]) {
|
||||
return m2.created_at.getTime() - m1.created_at.getTime();
|
||||
});
|
||||
}
|
||||
|
||||
// credit to GPT4
|
||||
export function findUrlInString(text: string): (string | URL)[] {
|
||||
// Regular expression for URLs with various protocols
|
||||
const urlRegex = /[a-zA-Z][a-zA-Z0-9+.-]*:\/\/[^\s]+/g;
|
||||
|
||||
// Split the text into URL and non-URL parts
|
||||
let parts = text.split(urlRegex);
|
||||
|
||||
// Find all URLs using the regex
|
||||
const foundUrls = text.match(urlRegex) || [];
|
||||
|
||||
// Interleave non-URL parts and URL parts
|
||||
let result: (string | URL)[] = [];
|
||||
parts.forEach((part, index) => {
|
||||
if (part !== "") {
|
||||
result.push(part);
|
||||
}
|
||||
if (index < foundUrls.length) {
|
||||
try {
|
||||
result.push(new URL(foundUrls[index]));
|
||||
} catch {
|
||||
result.push(foundUrls[index]);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ import { KeyIcon } from "./icons/key-icon.tsx";
|
||||
import { UserIcon } from "./icons/user-icon.tsx";
|
||||
import { CopyButton } from "./components/copy-button.tsx";
|
||||
import { LinkColor } from "./style/colors.ts";
|
||||
import { findUrlInString } from "./message.ts";
|
||||
|
||||
type UserDetailProps = {
|
||||
targetUserProfile: ProfileData;
|
||||
@ -20,7 +21,7 @@ type UserDetailProps = {
|
||||
|
||||
export function UserDetail(props: UserDetailProps) {
|
||||
return (
|
||||
<div class={tw`p-2 relative`}>
|
||||
<div class={tw`p-2 relative text-[#7A818C]`}>
|
||||
<Avatar
|
||||
class={tw`w-64 h-64 m-auto mt-8`}
|
||||
picture={props.targetUserProfile.picture}
|
||||
@ -68,9 +69,9 @@ export function UserDetail(props: UserDetailProps) {
|
||||
}}
|
||||
/>
|
||||
<p
|
||||
class={tw`flex-1 text-[#7A818C] group-hover:text-[#F3F4EA] break-words overflow-hidden`}
|
||||
class={tw`flex-1 break-words overflow-hidden`}
|
||||
>
|
||||
{props.targetUserProfile.about}
|
||||
{TextWithLinks({ text: props.targetUserProfile.about })}
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
@ -87,11 +88,9 @@ export function UserDetail(props: UserDetailProps) {
|
||||
}}
|
||||
/>
|
||||
<p
|
||||
class={tw`flex-1 text-[${LinkColor}] group-hover:text-[#F3F4EA] break-words overflow-hidden`}
|
||||
class={tw`flex-1 break-words overflow-hidden`}
|
||||
>
|
||||
<a href={props.targetUserProfile.website} target="_blank">
|
||||
{props.targetUserProfile.website}
|
||||
</a>
|
||||
{TextWithLinks({ text: props.targetUserProfile.website })}
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
@ -99,3 +98,29 @@ export function UserDetail(props: UserDetailProps) {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function TextWithLinks({ text }: { text: string }) {
|
||||
const parts = findUrlInString(text);
|
||||
|
||||
return (
|
||||
<div>
|
||||
{parts.map((part, index) => {
|
||||
if (part instanceof URL) {
|
||||
return (
|
||||
<a
|
||||
class={tw`text-[${LinkColor}] hover:text-[#F3F4EA]`}
|
||||
key={index}
|
||||
href={part.href}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{part.href}
|
||||
</a>
|
||||
);
|
||||
} else {
|
||||
return <span key={index}>{part}</span>;
|
||||
}
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user