update repost
This commit is contained in:
@@ -36,7 +36,7 @@ export function ChatsListItem({ data }: { data: any }) {
|
|||||||
<Image
|
<Image
|
||||||
src={user?.image || DEFAULT_AVATAR}
|
src={user?.image || DEFAULT_AVATAR}
|
||||||
alt={data.sender_pubkey}
|
alt={data.sender_pubkey}
|
||||||
className="h-5 w-5 rounded bg-white object-cover"
|
className="h-5 w-5 rounded object-cover"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="w-full inline-flex items-center justify-between">
|
<div className="w-full inline-flex items-center justify-between">
|
||||||
|
|||||||
@@ -8,33 +8,31 @@ import {
|
|||||||
ComposeIcon,
|
ComposeIcon,
|
||||||
} from "@shared/icons";
|
} from "@shared/icons";
|
||||||
import { useActiveAccount } from "@stores/accounts";
|
import { useActiveAccount } from "@stores/accounts";
|
||||||
import { Fragment, useState } from "react";
|
import { useComposer } from "@stores/composer";
|
||||||
|
import { Fragment } from "react";
|
||||||
export function ComposerModal() {
|
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
|
||||||
const [composer] = useState({ type: "post" });
|
|
||||||
|
|
||||||
|
export function Composer() {
|
||||||
const account = useActiveAccount((state: any) => state.account);
|
const account = useActiveAccount((state: any) => state.account);
|
||||||
|
const [toggle, open] = useComposer((state: any) => [
|
||||||
|
state.toggleModal,
|
||||||
|
state.open,
|
||||||
|
]);
|
||||||
|
|
||||||
const closeModal = () => {
|
const closeModal = () => {
|
||||||
setIsOpen(false);
|
toggle(false);
|
||||||
};
|
|
||||||
|
|
||||||
const openModal = () => {
|
|
||||||
setIsOpen(true);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => openModal()}
|
onClick={() => toggle(true)}
|
||||||
className="inline-flex h-8 w-max items-center justify-center gap-1 rounded-md bg-fuchsia-500 px-2.5 text-sm font-medium text-zinc-100 shadow-button hover:bg-fuchsia-600 focus:outline-none"
|
className="inline-flex h-8 w-max items-center justify-center gap-1 rounded-md bg-fuchsia-500 px-2.5 text-sm font-medium text-zinc-100 shadow-button hover:bg-fuchsia-600 focus:outline-none"
|
||||||
>
|
>
|
||||||
<ComposeIcon width={14} height={14} />
|
<ComposeIcon width={14} height={14} />
|
||||||
Compose
|
Compose
|
||||||
</button>
|
</button>
|
||||||
<Transition appear show={isOpen} as={Fragment}>
|
<Transition appear show={open} as={Fragment}>
|
||||||
<Dialog as="div" className="relative z-10" onClose={closeModal}>
|
<Dialog as="div" className="relative z-10" onClose={closeModal}>
|
||||||
<Transition.Child
|
<Transition.Child
|
||||||
as={Fragment}
|
as={Fragment}
|
||||||
@@ -68,7 +66,7 @@ export function ComposerModal() {
|
|||||||
className="text-zinc-500"
|
className="text-zinc-500"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
<div className="inline-flex h-6 w-max items-center justify-center gap-0.5 rounded bg-zinc-800 pl-3 pr-1.5 text-base font-medium text-zinc-400 shadow-mini-button">
|
<div className="inline-flex h-6 w-max items-center justify-center gap-0.5 rounded bg-zinc-800 pl-3 pr-1.5 text-sm font-medium text-zinc-400">
|
||||||
New Post
|
New Post
|
||||||
<ChevronDownIcon width={14} height={14} />
|
<ChevronDownIcon width={14} height={14} />
|
||||||
</div>
|
</div>
|
||||||
@@ -85,9 +83,7 @@ export function ComposerModal() {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{composer.type === "post" && account && (
|
|
||||||
<Post pubkey={account.pubkey} privkey={account.privkey} />
|
<Post pubkey={account.pubkey} privkey={account.privkey} />
|
||||||
)}
|
|
||||||
</Dialog.Panel>
|
</Dialog.Panel>
|
||||||
</Transition.Child>
|
</Transition.Child>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
import { NDKEvent, NDKPrivateKeySigner } from "@nostr-dev-kit/ndk";
|
import { NDKEvent, NDKPrivateKeySigner } from "@nostr-dev-kit/ndk";
|
||||||
import { ImageUploader } from "@shared/composer/imageUploader";
|
import { ImageUploader } from "@shared/composer/imageUploader";
|
||||||
import { TrashIcon } from "@shared/icons";
|
import { TrashIcon } from "@shared/icons";
|
||||||
|
import { MentionNote } from "@shared/notes/mentions/note";
|
||||||
import { RelayContext } from "@shared/relayProvider";
|
import { RelayContext } from "@shared/relayProvider";
|
||||||
|
import { useComposer } from "@stores/composer";
|
||||||
import { dateToUnix } from "@utils/date";
|
import { dateToUnix } from "@utils/date";
|
||||||
import { useCallback, useContext, useMemo, useState } from "react";
|
import { useCallback, useContext, useMemo, useState } from "react";
|
||||||
import { Node, Transforms, createEditor } from "slate";
|
import { Node, Transforms, createEditor } from "slate";
|
||||||
@@ -59,7 +61,10 @@ const ImagePreview = ({
|
|||||||
|
|
||||||
export function Post({ pubkey, privkey }: { pubkey: string; privkey: string }) {
|
export function Post({ pubkey, privkey }: { pubkey: string; privkey: string }) {
|
||||||
const ndk = useContext(RelayContext);
|
const ndk = useContext(RelayContext);
|
||||||
|
const [repost, toggle] = useComposer((state: any) => [
|
||||||
|
state.repost,
|
||||||
|
state.toggle,
|
||||||
|
]);
|
||||||
const editor = useMemo(
|
const editor = useMemo(
|
||||||
() => withReact(withImages(withHistory(createEditor()))),
|
() => withReact(withImages(withHistory(createEditor()))),
|
||||||
[],
|
[],
|
||||||
@@ -87,14 +92,25 @@ export function Post({ pubkey, privkey }: { pubkey: string; privkey: string }) {
|
|||||||
ndk.signer = signer;
|
ndk.signer = signer;
|
||||||
|
|
||||||
const event = new NDKEvent(ndk);
|
const event = new NDKEvent(ndk);
|
||||||
event.kind = 1;
|
|
||||||
event.content = serializedContent;
|
event.content = serializedContent;
|
||||||
event.created_at = dateToUnix();
|
event.created_at = dateToUnix();
|
||||||
event.pubkey = pubkey;
|
event.pubkey = pubkey;
|
||||||
|
if (repost.id && repost.pubkey) {
|
||||||
|
event.kind = 6;
|
||||||
|
event.tags = [
|
||||||
|
["e", repost.id],
|
||||||
|
["p", repost.pubkey],
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
event.kind = 1;
|
||||||
event.tags = [];
|
event.tags = [];
|
||||||
|
}
|
||||||
|
|
||||||
// publish event
|
// publish event
|
||||||
event.publish();
|
event.publish();
|
||||||
|
|
||||||
|
// close modal
|
||||||
|
toggle(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderElement = useCallback((props: any) => {
|
const renderElement = useCallback((props: any) => {
|
||||||
@@ -115,17 +131,20 @@ export function Post({ pubkey, privkey }: { pubkey: string; privkey: string }) {
|
|||||||
<div className="flex w-8 shrink-0 items-center justify-center">
|
<div className="flex w-8 shrink-0 items-center justify-center">
|
||||||
<div className="h-full w-[2px] bg-zinc-800" />
|
<div className="h-full w-[2px] bg-zinc-800" />
|
||||||
</div>
|
</div>
|
||||||
<div className="w-full markdown">
|
<div className="w-full">
|
||||||
<Editable
|
<Editable
|
||||||
autoFocus
|
autoFocus
|
||||||
placeholder="What's on your mind?"
|
placeholder="What's on your mind?"
|
||||||
spellCheck="false"
|
spellCheck="false"
|
||||||
className="!min-h-[86px]"
|
className={`${
|
||||||
|
repost.id ? "!min-h-42" : "!min-h-[86px]"
|
||||||
|
} markdown`}
|
||||||
renderElement={renderElement}
|
renderElement={renderElement}
|
||||||
/>
|
/>
|
||||||
|
{repost.id && <MentionNote id={repost.id} />}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center justify-between">
|
<div className="mt-4 flex items-center justify-between">
|
||||||
<ImageUploader />
|
<ImageUploader />
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
|||||||
@@ -3,21 +3,19 @@ import { ChatsList } from "@app/chat/components/list";
|
|||||||
import { Disclosure } from "@headlessui/react";
|
import { Disclosure } from "@headlessui/react";
|
||||||
import { ActiveLink } from "@shared/activeLink";
|
import { ActiveLink } from "@shared/activeLink";
|
||||||
import { AppHeader } from "@shared/appHeader";
|
import { AppHeader } from "@shared/appHeader";
|
||||||
import { ComposerModal } from "@shared/composer/modal";
|
import { Composer } from "@shared/composer/modal";
|
||||||
import {
|
import { NavArrowDownIcon, SpaceIcon, TrendingIcon } from "@shared/icons";
|
||||||
NavArrowDownIcon,
|
import { useComposer } from "@stores/composer";
|
||||||
SpaceIcon,
|
|
||||||
TrendingIcon,
|
|
||||||
WorldIcon,
|
|
||||||
} from "@shared/icons";
|
|
||||||
|
|
||||||
export function Navigation() {
|
export function Navigation() {
|
||||||
|
const toggle = useComposer((state: any) => state.toggleModal);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex w-[232px] flex-col gap-3 border-r border-zinc-900">
|
<div className="flex w-[232px] flex-col gap-3 border-r border-zinc-900">
|
||||||
<AppHeader />
|
<AppHeader />
|
||||||
<div className="flex flex-col gap-3 h-full overflow-y-auto scrollbar-hide">
|
<div className="flex flex-col gap-3 h-full overflow-y-auto scrollbar-hide">
|
||||||
<div className="inlin-lflex h-8 px-3.5">
|
<div className="inlin-lflex h-8 px-3.5">
|
||||||
<ComposerModal />
|
<Composer />
|
||||||
</div>
|
</div>
|
||||||
{/* Newsfeed */}
|
{/* Newsfeed */}
|
||||||
<div className="flex flex-col gap-0.5 px-1.5">
|
<div className="flex flex-col gap-0.5 px-1.5">
|
||||||
|
|||||||
@@ -10,8 +10,12 @@ export function Kind1({
|
|||||||
}: { content: any; truncate?: boolean }) {
|
}: { content: any; truncate?: boolean }) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="select-text whitespace-pre-line break-words text-base text-zinc-100">
|
<div
|
||||||
{truncate ? truncateContent(content.original, 100) : content.parsed}
|
className={`select-text whitespace-pre-line break-words text-base text-zinc-100 ${
|
||||||
|
truncate ? "line-clamp-3" : ""
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{content.parsed}
|
||||||
</div>
|
</div>
|
||||||
{Array.isArray(content.images) && content.images.length ? (
|
{Array.isArray(content.images) && content.images.length ? (
|
||||||
<ImagePreview urls={content.images} />
|
<ImagePreview urls={content.images} />
|
||||||
|
|||||||
@@ -1,49 +1,18 @@
|
|||||||
import { NDKEvent, NDKPrivateKeySigner } from "@nostr-dev-kit/ndk";
|
|
||||||
import { RepostIcon } from "@shared/icons";
|
import { RepostIcon } from "@shared/icons";
|
||||||
import { RelayContext } from "@shared/relayProvider";
|
import { useComposer } from "@stores/composer";
|
||||||
import { useActiveAccount } from "@stores/accounts";
|
|
||||||
import { dateToUnix } from "@utils/date";
|
|
||||||
import { compactNumber } from "@utils/number";
|
import { compactNumber } from "@utils/number";
|
||||||
import { useContext, useState } from "react";
|
|
||||||
|
|
||||||
export function NoteRepost({
|
export function NoteRepost({
|
||||||
id,
|
id,
|
||||||
pubkey,
|
pubkey,
|
||||||
reposts,
|
reposts,
|
||||||
}: { id: string; pubkey: string; reposts: number }) {
|
}: { id: string; pubkey: string; reposts: number }) {
|
||||||
const ndk = useContext(RelayContext);
|
const setRepost = useComposer((state: any) => state.setRepost);
|
||||||
const account = useActiveAccount((state: any) => state.account);
|
|
||||||
|
|
||||||
const [count, setCount] = useState(reposts);
|
|
||||||
|
|
||||||
const submitEvent = (e: any) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
|
|
||||||
const signer = new NDKPrivateKeySigner(account.privkey);
|
|
||||||
ndk.signer = signer;
|
|
||||||
|
|
||||||
const event = new NDKEvent(ndk);
|
|
||||||
// build event
|
|
||||||
event.content = "";
|
|
||||||
event.kind = 6;
|
|
||||||
event.created_at = dateToUnix();
|
|
||||||
event.pubkey = account.pubkey;
|
|
||||||
event.tags = [
|
|
||||||
["e", id],
|
|
||||||
["p", pubkey],
|
|
||||||
];
|
|
||||||
|
|
||||||
// publish event
|
|
||||||
event.publish();
|
|
||||||
|
|
||||||
// update state
|
|
||||||
setCount(count + 1);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={(e) => submitEvent(e)}
|
onClick={() => setRepost(id, pubkey)}
|
||||||
className="w-20 group inline-flex items-center gap-1.5"
|
className="w-20 group inline-flex items-center gap-1.5"
|
||||||
>
|
>
|
||||||
<RepostIcon
|
<RepostIcon
|
||||||
@@ -52,7 +21,7 @@ export function NoteRepost({
|
|||||||
className="text-zinc-400 group-hover:text-blue-400"
|
className="text-zinc-400 group-hover:text-blue-400"
|
||||||
/>
|
/>
|
||||||
<span className="text-base leading-none text-zinc-400 group-hover:text-zinc-100">
|
<span className="text-base leading-none text-zinc-400 group-hover:text-zinc-100">
|
||||||
{compactNumber.format(count)}
|
{compactNumber.format(reposts)}
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -27,12 +27,14 @@ export function User({
|
|||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<Popover.Button
|
<Popover.Button
|
||||||
className={`${avatarWidth} ${avatarHeight} shrink-0 overflow-hidden rounded-md bg-zinc-900`}
|
className={`${avatarWidth} ${avatarHeight} shrink-0 overflow-hidden`}
|
||||||
>
|
>
|
||||||
<Image
|
<Image
|
||||||
src={user?.image || DEFAULT_AVATAR}
|
src={user?.image || DEFAULT_AVATAR}
|
||||||
alt={pubkey}
|
alt={pubkey}
|
||||||
className={`${avatarWidth} ${avatarHeight} rounded-md object-cover`}
|
className={`${avatarWidth} ${avatarHeight} ${
|
||||||
|
size === "small" ? "rounded" : "rounded-md"
|
||||||
|
} object-cover`}
|
||||||
/>
|
/>
|
||||||
</Popover.Button>
|
</Popover.Button>
|
||||||
<div className="flex flex-wrap items-baseline gap-1">
|
<div className="flex flex-wrap items-baseline gap-1">
|
||||||
|
|||||||
20
src/stores/composer.tsx
Normal file
20
src/stores/composer.tsx
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import { create } from "zustand";
|
||||||
|
|
||||||
|
export const useComposer = create((set) => ({
|
||||||
|
open: false,
|
||||||
|
repost: { id: null, pubkey: null },
|
||||||
|
reply: null,
|
||||||
|
toggleModal: (status: boolean) => {
|
||||||
|
set({ open: status });
|
||||||
|
if (!status) {
|
||||||
|
set({ repost: { id: null, pubkey: null } });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
setRepost: (id: string, pubkey: string) => {
|
||||||
|
set({ repost: { id: id, pubkey: pubkey } });
|
||||||
|
set({ open: true });
|
||||||
|
},
|
||||||
|
clearRepost: () => {
|
||||||
|
set({ repost: { id: null, pubkey: null } });
|
||||||
|
},
|
||||||
|
}));
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
export const APP_VERSION = "1.0.0";
|
export const APP_VERSION = "1.0.0";
|
||||||
|
|
||||||
export const DEFAULT_AVATAR = "https://void.cat/d/PZcdCxNc24rCCxV8QXbdFQ";
|
export const DEFAULT_AVATAR = "https://void.cat/d/5VKmKyuHyxrNMf9bWSVPih";
|
||||||
|
|
||||||
export const OPENGRAPH_KEY = "9EJG4SY-19Q4M5J-H8R29C9-091XPCC";
|
export const OPENGRAPH_KEY = "9EJG4SY-19Q4M5J-H8R29C9-091XPCC";
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user