feat: polish

This commit is contained in:
reya 2024-01-19 08:24:13 +07:00
parent 16efd495a0
commit f65175f11e
10 changed files with 114 additions and 27 deletions

View File

@ -40,8 +40,8 @@ const Item = ({ event }: { event: NDKEvent }) => {
export function CreateAccountScreen() {
const ark = useArk();
const storage = useStorage();
const navigate = useNavigate();
const services = useLoaderData() as NDKEvent[];
const navigate = useNavigate();
const setOnboarding = useSetAtom(onboardingAtom);
const [serviceId, setServiceId] = useState(services?.[0]?.id);
@ -162,7 +162,7 @@ export function CreateAccountScreen() {
ark.updateNostrSigner({ signer: finalSigner });
// remove default nsecbunker profile and contact list
await ark.createEvent({ kind: NDKKind.Metadata, content: "", tags: [] });
// await ark.createEvent({ kind: NDKKind.Metadata, content: "", tags: [] });
await ark.createEvent({ kind: NDKKind.Contacts, content: "", tags: [] });
setOnboarding(true);

View File

@ -276,7 +276,7 @@ export class Ark {
if (content.includes("nostr:note1") || content.includes("nostr:nevent1"))
return null;
const events = tags.filter((el) => el[0] === "e");
const events = tags.filter((el) => el[0] === "e" && el[3] !== "mention");
if (!events.length) return null;
@ -325,7 +325,9 @@ export class Ark {
if (events.length > 0) {
const replies = new Set();
for (const event of events) {
const tags = event.tags.filter((el) => el[0] === "e" && el[1] !== id);
const tags = event.tags.filter(
(el) => el[0] === "e" && el[1] !== id && el[3] !== "mention",
);
if (tags.length > 0) {
for (const tag of tags) {
const rootIndex = events.findIndex((el) => el.id === tag[1]);

View File

@ -1,11 +1,13 @@
import { PinIcon, RefreshIcon } from "@lume/icons";
import { COL_TYPES } from "@lume/utils";
import { memo } from "react";
import { PinIcon } from "@lume/icons";
import { COL_TYPES, NOSTR_MENTIONS } from "@lume/utils";
import { ReactNode, memo, useMemo } from "react";
import { Link } from "react-router-dom";
import { Note } from "../";
import reactStringReplace from "react-string-replace";
import { useEvent } from "../../../hooks/useEvent";
import { useColumnContext } from "../../column/provider";
import { User } from "../../user";
import { Hashtag } from "./hashtag";
import { MentionUser } from "./user";
export const MentionNote = memo(function MentionNote({
eventId,
@ -14,6 +16,71 @@ export const MentionNote = memo(function MentionNote({
const { addColumn } = useColumnContext();
const { isLoading, isError, data } = useEvent(eventId);
const richContent = useMemo(() => {
if (!data) return "";
let parsedContent: string | ReactNode[] = data.content.replace(
/\n+/g,
"\n",
);
const text = parsedContent as string;
const words = text.split(/( |\n)/);
const hashtags = words.filter((word) => word.startsWith("#"));
const mentions = words.filter((word) =>
NOSTR_MENTIONS.some((el) => word.startsWith(el)),
);
try {
if (hashtags.length) {
for (const hashtag of hashtags) {
parsedContent = reactStringReplace(
parsedContent,
hashtag,
(match, i) => {
return <Hashtag key={match + i} tag={hashtag} />;
},
);
}
}
if (mentions.length) {
for (const mention of mentions) {
parsedContent = reactStringReplace(
parsedContent,
mention,
(match, i) => <MentionUser key={match + i} pubkey={mention} />,
);
}
}
parsedContent = reactStringReplace(
parsedContent,
/(https?:\/\/\S+)/g,
(match, i) => {
const url = new URL(match);
return (
<Link
key={match + i}
to={url.toString()}
target="_blank"
rel="noreferrer"
className="break-p font-normal text-blue-500 hover:text-blue-600"
>
{url.toString()}
</Link>
);
},
);
return parsedContent;
} catch (e) {
console.log(e);
return parsedContent;
}
}, [data]);
if (isLoading) {
return (
<div
@ -31,14 +98,14 @@ export const MentionNote = memo(function MentionNote({
contentEditable={false}
className="w-full p-3 my-1 rounded-lg cursor-default bg-neutral-100 dark:bg-neutral-900"
>
Failed to fetch event
Failed to fetch event.
</div>
);
}
return (
<Note.Provider event={data}>
<Note.Root className="flex flex-col w-full my-1 rounded-lg cursor-default bg-neutral-100 dark:bg-neutral-900 border border-neutral-100 dark:border-neutral-900">
<div>
<div className="flex flex-col w-full my-1 rounded-lg cursor-default bg-neutral-100 dark:bg-neutral-900 border border-neutral-100 dark:border-neutral-900">
<User.Provider pubkey={data.pubkey}>
<User.Root className="flex h-10 px-3 items-center gap-2">
<User.Avatar className="size-6 shrink-0 rounded-md object-cover" />
@ -52,7 +119,9 @@ export const MentionNote = memo(function MentionNote({
</div>
</User.Root>
</User.Provider>
<Note.Content mini className="px-3" />
<div className="px-3 select-text text-balance leading-normal line-clamp-4 whitespace-pre-line">
{richContent}
</div>
{openable ? (
<div className="px-3 h-10 flex items-center justify-between">
<Link
@ -76,9 +145,9 @@ export const MentionNote = memo(function MentionNote({
</button>
</div>
) : (
<div className="h-10" />
<div className="h-3" />
)}
</Note.Root>
</Note.Provider>
</div>
</div>
);
});

View File

@ -38,6 +38,7 @@ export function Reply({
<div />
)}
<div className="inline-flex items-center gap-4">
<Note.Reply />
<Note.Repost />
<Note.Zap />
</div>

View File

@ -6,7 +6,7 @@ export function UserAbout({ className }: { className?: string }) {
if (!user) {
return (
<>
<div className="flex flex-col gap-1">
<div
className={cn(
"h-4 w-20 bg-black/20 dark:bg-white/20 rounded animate-pulse",
@ -25,7 +25,7 @@ export function UserAbout({ className }: { className?: string }) {
className,
)}
/>
</>
</div>
);
}

View File

@ -15,9 +15,17 @@ export function UserCover({ className }: { className?: string }) {
);
}
if (user && !user.banner) {
return (
<div
className={cn("bg-gradient-to-b from-sky-400 to-sky-200", className)}
/>
);
}
return (
<img
src={user.banner || user.cover}
src={user.banner}
alt="banner"
loading="lazy"
decoding="async"

View File

@ -169,8 +169,11 @@ export const LumeProvider = ({ children }: PropsWithChildren<object>) => {
);
activitySub.addListener("event", async (event: NDKEvent) => {
if (event.pubkey === storage.currentUser.pubkey) return;
setUnreadActivity((state) => state + 1);
const profile = await ark.getUserProfile(event.pubkey);
switch (event.kind) {
case NDKKind.Text:
return await sendNativeNotification(

View File

@ -2,10 +2,9 @@ import { MentionNote, User, useArk } from "@lume/ark";
import { LoaderIcon, TrashIcon } from "@lume/icons";
import { useStorage } from "@lume/storage";
import { NDKCacheUserProfile } from "@lume/types";
import { cn, editorValueAtom } from "@lume/utils";
import { cn } from "@lume/utils";
import { NDKEvent, NDKKind } from "@nostr-dev-kit/ndk";
import { Portal } from "@radix-ui/react-dropdown-menu";
import { useAtom } from "jotai";
import { useEffect, useRef, useState } from "react";
import {
Descendant,
@ -193,7 +192,12 @@ export function ReplyForm({
const storage = useStorage();
const ref = useRef<HTMLDivElement | null>();
const [editorValue, setEditorValue] = useAtom(editorValueAtom);
const [editorValue, setEditorValue] = useState([
{
type: "paragraph",
children: [{ text: "" }],
},
]);
const [contacts, setContacts] = useState<NDKCacheUserProfile[]>([]);
const [target, setTarget] = useState<Range | undefined>();
const [index, setIndex] = useState(0);

View File

@ -218,7 +218,6 @@ export function OnboardingFollowScreen() {
</>
)}
</button>
UU
</div>
</div>
))

View File

@ -23,7 +23,7 @@ export function OnboardingProfileSettingsScreen() {
const { register, handleSubmit } = useForm();
const svgURI = `data:image/svg+xml;utf8,${encodeURIComponent(
minidenticon("lume new account", 90, 50),
minidenticon(ark.account.pubkey, 90, 50),
)}`;
const onSubmit = async (data: { name: string; about: string }) => {
@ -39,7 +39,7 @@ export function OnboardingProfileSettingsScreen() {
const profile: NDKUserProfile = {
...data,
lud16: oldProfile?.lud16 || "",
lud16: "", // temporary remove lud16
nip05: oldProfile?.nip05 || "",
display_name: data.name,
bio: data.about,
@ -56,9 +56,10 @@ export function OnboardingProfileSettingsScreen() {
if (publish) {
// invalid cache
await storage.clearProfileCache(ark.account.pubkey);
await queryClient.setQueryData(["user", ark.account.pubkey], () => {
return profile;
});
await queryClient.setQueryData(
["user", ark.account.pubkey],
() => profile,
);
setLoading(false);
navigate("/follow");