Skip to content

Commit

Permalink
Book app update (#43)
Browse files Browse the repository at this point in the history
* chore(book-app): add a copy address button in connector

* chore(book-app): add prettier for fmt
  • Loading branch information
RetricSu authored Aug 2, 2024
1 parent db4add8 commit 7f0d669
Show file tree
Hide file tree
Showing 20 changed files with 369 additions and 251 deletions.
14 changes: 7 additions & 7 deletions packages/book-app/components/book/add-book-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,15 +72,15 @@ const AddBookCard: React.FC = () => {
const result = await buildMintTransaction(
pubkey,
ckbSigner.ckbAddress,
eventToBind
eventToBind,
);
const txHash = await mint(result);

setShowPopup(false);
};

const mint = async (
result: Awaited<ReturnType<typeof buildMintTransaction>>
result: Awaited<ReturnType<typeof buildMintTransaction>>,
) => {
if (!nostrWriteClient || !nostrSigner || !ckbSigner) {
throw new Error("no signer/client found!");
Expand All @@ -89,19 +89,19 @@ const AddBookCard: React.FC = () => {
const mintEvent = result.mintEvent;

const signedMintEvent = await nostrSigner.signEvent(
UnsignedEvent.fromJson(JSON.stringify(mintEvent))
UnsignedEvent.fromJson(JSON.stringify(mintEvent)),
);
const mintEventWitness = bytes.hexify(
jsonStringToBytes(signedMintEvent.asJson())
jsonStringToBytes(signedMintEvent.asJson()),
);
const witness = bytes.hexify(
blockchain.WitnessArgs.pack({
outputType: mintEventWitness,
})
}),
);
txSkeleton = txSkeleton.update(
"witnesses",
(witnesses: Immutable.List<string>) => witnesses.set(0, witness)
(witnesses: Immutable.List<string>) => witnesses.set(0, witness),
);
const tx = createTransactionFromSkeleton(txSkeleton);
const signedTx = await ckbSigner.signTransaction(tx);
Expand Down Expand Up @@ -185,7 +185,7 @@ const AddBookCard: React.FC = () => {
</div>
<div className="mb-4">
<label className="block text-sm font-medium text-gray-700">
Chapters: 30041 Event IDs
Chapters: 30041 Event IDs
<Link
href={
"https://next.nostrudel.ninja/#/wiki/topic/nkbip-01?pubkey=dd664d5e4016433a8cd69f005ae1480804351789b59de5af06276de65633d319"
Expand Down
37 changes: 20 additions & 17 deletions packages/book-app/components/book/all-books.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,24 @@
import { Event, Filter } from "@rust-nostr/nostr-sdk";
import BookCard from "./book-card";
import { useCallback, useContext, useEffect, useState } from "react";
import { isValidBookEvent, getEvents, parseBookFromEvent } from "../../lib/nostr";
import {
isValidBookEvent,
getEvents,
parseBookFromEvent,
} from "../../lib/nostr";
import { NostrClientContext } from "../../context/nostr-client";

export function AllBooks() {

const { nostrReadClient } =
useContext(NostrClientContext);
const { nostrReadClient } = useContext(NostrClientContext);

const [events, setEvents] = useState<Event[]>([]);
const [events, setEvents] = useState<Event[]>([]);

const getBookEvents = useCallback(async () => {
if (!nostrReadClient)return;
if (!nostrReadClient) return;

const filter = new Filter().kind(30040).limit(10);
const events = await getEvents(nostrReadClient, [filter]);
setEvents((pre) => [...pre, ...events.filter(e => isValidBookEvent(e))]);
const events = await getEvents(nostrReadClient, [filter]);
setEvents((pre) => [...pre, ...events.filter((e) => isValidBookEvent(e))]);
}, [nostrReadClient]);

useEffect(() => {
Expand All @@ -28,15 +30,16 @@ export function AllBooks() {
{events.map((event) => {
const book = parseBookFromEvent(event)!;
return (
<BookCard
key={event.id.toHex()}
eventId={event.id.toHex()}
title={book.title}
author={book.author!}
description={book.summary!}
imageUrl={book.image!}
/>
)})}
<BookCard
key={event.id.toHex()}
eventId={event.id.toHex()}
title={book.title}
author={book.author!}
description={book.summary!}
imageUrl={book.image!}
/>
);
})}
</div>
);
}
34 changes: 18 additions & 16 deletions packages/book-app/components/book/my-books.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Filter, Event } from "@rust-nostr/nostr-sdk";
import { Filter, Event } from "@rust-nostr/nostr-sdk";
import { useCallback, useContext, useEffect, useState } from "react";
import { SingerContext } from "../../context/signer";
import AddBookCard from "./add-book-card";
Expand All @@ -8,24 +8,25 @@ import { getEvents, isValidBookEvent, parseBookFromEvent } from "@/lib/nostr";

export function MyBook() {
const [events, setEvents] = useState<Event[]>([]);
const { nostrReadClient } =
useContext(NostrClientContext);
const { nostrReadClient } = useContext(NostrClientContext);

const {nostrSigner} = useContext(SingerContext);
const { nostrSigner } = useContext(SingerContext);

const getBookEvents = useCallback(async () => {
if (nostrReadClient && nostrSigner) {
const pubkey =await nostrSigner.publicKey();
const pubkey = await nostrSigner.publicKey();
const filter = new Filter().author(pubkey).kind(30040).limit(10);
const filters = [filter];
const events = await getEvents(nostrReadClient, filters);
setEvents((pre) => [...pre, ...events.filter(e => isValidBookEvent(e))]);
setEvents((pre) => [
...pre,
...events.filter((e) => isValidBookEvent(e)),
]);
return events;
}
return [];
}, [nostrReadClient, nostrSigner]);


useEffect(() => {
getBookEvents();
}, [getBookEvents]);
Expand All @@ -35,15 +36,16 @@ export function MyBook() {
{events.map((event) => {
const book = parseBookFromEvent(event)!;
return (
<BookCard
key={event.id.toHex()}
eventId={event.id.toHex()}
title={book.title}
author={book.author!}
description={book.summary!}
imageUrl={book.image!}
/>
)})}
<BookCard
key={event.id.toHex()}
eventId={event.id.toHex()}
title={book.title}
author={book.author!}
description={book.summary!}
imageUrl={book.image!}
/>
);
})}
<AddBookCard />
</div>
);
Expand Down
23 changes: 22 additions & 1 deletion packages/book-app/components/wallet/connect-nostr.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -95,10 +95,16 @@ const Popup: React.FC<PopupProps> = ({
if (!ckbAddress) return;

capacityOf(ckbAddress).then((bal) =>
setBalance(bal.div(100000000).toString())
setBalance(bal.div(100000000).toString()),
);
}, [ckbAddress]);

const handleCopy = () => {
if (!ckbAddress) return alert("ckb address not found!");
navigator.clipboard.writeText(ckbAddress);
alert("Address copied to clipboard");
};

if (!show) return null;
return (
<div className="fixed inset-0 bg-gray-600 bg-opacity-50 flex justify-center items-center">
Expand All @@ -117,6 +123,21 @@ const Popup: React.FC<PopupProps> = ({

<div className="text-2xl font-bold my-2">
{buildTruncateCkbAddress(ckbAddress || "CKB Address Not Found")}
{ckbAddress && (
<button
onClick={handleCopy}
className="text-gray-600 hover:text-gray-800 focus:outline-none mx-2"
>
<svg
xmlns="http://www.w3.org/2000/svg"
className="h-5 w-5"
viewBox="0 0 24 24"
fill="currentColor"
>
<path d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z" />
</svg>
</button>
)}
</div>

<div className="text-lg mt-4 mb-6">{balance} CKB</div>
Expand Down
23 changes: 14 additions & 9 deletions packages/book-app/context/signer.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
import { sdk } from "@/lib/sdk";
import { CellDep, helpers, Script, Transaction } from "@ckb-lumos/lumos";
import { EventToSign, SignedEvent } from "@nostr-binding/sdk";
import { EventBuilder, NostrSigner, PublicKey, Tag, Timestamp, UnsignedEvent } from "@rust-nostr/nostr-sdk";
import {
EventBuilder,
NostrSigner,
PublicKey,
Tag,
Timestamp,
UnsignedEvent,
} from "@rust-nostr/nostr-sdk";
import { createContext } from "react";

export interface CKBSigner {
Expand Down Expand Up @@ -39,7 +46,7 @@ export const SingerContext =

export const buildNostrCKBSigner = async (
publicKey: PublicKey,
nostrSigner: NostrSigner
nostrSigner: NostrSigner,
) => {
// update ckb signer context
const signMessage = async (message: string) => {
Expand All @@ -54,10 +61,9 @@ export const buildNostrCKBSigner = async (
const eventBuilder = new EventBuilder(
event.kind,
event.content,
event.tags.map((tag) => Tag.parse(tag))
event.tags.map((tag) => Tag.parse(tag)),
).customCreatedAt(Timestamp.fromSecs(event.created_at));
const nostrSignedEvent =
await nostrSigner.signEventBuilder(eventBuilder);
const nostrSignedEvent = await nostrSigner.signEventBuilder(eventBuilder);
const signedEvent: SignedEvent = JSON.parse(nostrSignedEvent.asJson());
return signedEvent;
};
Expand All @@ -66,16 +72,15 @@ export const buildNostrCKBSigner = async (

const signPreparedTransaction = async (
tx: Transaction,
lockIndexes: Array<number>
lockIndexes: Array<number>,
) => {
const signer = async (event: EventToSign) => {
const eventBuilder = new EventBuilder(
event.kind,
event.content,
event.tags.map((tag) => Tag.parse(tag))
event.tags.map((tag) => Tag.parse(tag)),
).customCreatedAt(Timestamp.fromSecs(event.created_at));
const nostrSignedEvent =
await nostrSigner.signEventBuilder(eventBuilder);
const nostrSignedEvent = await nostrSigner.signEventBuilder(eventBuilder);
const signedEvent: SignedEvent = JSON.parse(nostrSignedEvent.asJson());
return signedEvent;
};
Expand Down
30 changes: 15 additions & 15 deletions packages/book-app/lib/ckb.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
import offCKB from "@/offckb.config";
import { blockchain } from "@ckb-lumos/lumos/codec";
import { bytes } from "@ckb-lumos/lumos/codec";
import { PublicKey, Timestamp } from "@rust-nostr/nostr-sdk";
import { PublicKey } from "@rust-nostr/nostr-sdk";
import { sdk } from "./sdk";
import { EventToBind } from "@nostr-binding/sdk";

Expand All @@ -23,7 +23,7 @@ const indexer = offCKB.indexer;
export async function buildUnlockCKBTransaction(
nostrPublicKey: PublicKey,
newLock: Script,
type: Script | undefined
type: Script | undefined,
) {
const ckbAddress = sdk.lock.encodeToCKBAddress("0x" + nostrPublicKey.toHex());

Expand All @@ -44,11 +44,11 @@ export async function buildUnlockCKBTransaction(
const txCellDeps = await sdk.lock.buildCellDeps();

txSkeleton = txSkeleton.update("inputs", (inputs) =>
inputs.push(...collectedInputs)
inputs.push(...collectedInputs),
);
txSkeleton = txSkeleton.update("outputs", (outputs) => outputs.push(output));
txSkeleton = txSkeleton.update("cellDeps", (cellDeps) =>
cellDeps.concat(txCellDeps)
cellDeps.concat(txCellDeps),
);

return txSkeleton;
Expand All @@ -57,7 +57,7 @@ export async function buildUnlockCKBTransaction(
export async function buildMintTransaction(
receiverNostrPublicKey: PublicKey,
minerCKBAddress: string,
eventToBind: EventToBind
eventToBind: EventToBind,
) {
let txSkeleton = helpers.TransactionSkeleton({});
const neededCapacity = BI.from(16000000000);
Expand All @@ -66,23 +66,23 @@ export async function buildMintTransaction(
const collectedInputs = await collectCell(minerCKBAddress, neededCapacity);
const globalUniqueId = sdk.binding.buildGlobalUniqueId(
collectedInputs[0],
"0x0"
"0x0",
);

const mintEvent = sdk.binding.finalizeEventToBind(
globalUniqueId,
eventToBind
eventToBind,
);

const lock = sdk.lock.buildScript("0x" + receiverNostrPublicKey.toHex());
const bindingCell = sdk.binding.buildBindingCell(
mintEvent.id!,
globalUniqueId,
lock
lock,
);

txSkeleton = txSkeleton.update("outputs", (outputs) =>
outputs.push(bindingCell)
outputs.push(bindingCell),
);

let collectedSum = BI.from(0);
Expand All @@ -100,17 +100,17 @@ export async function buildMintTransaction(
data: "0x",
};
txSkeleton = txSkeleton.update("outputs", (outputs) =>
outputs.push(changeOutput)
outputs.push(changeOutput),
);
}
txSkeleton = txSkeleton.update("inputs", (inputs) =>
inputs.push(...collectedInputs)
inputs.push(...collectedInputs),
);

const bindingDep = await sdk.binding.buildCellDeps();
const lockDep = await sdk.lock.buildCellDeps();
txSkeleton = txSkeleton.update("cellDeps", (cellDeps) =>
cellDeps.concat(lockDep, bindingDep)
cellDeps.concat(lockDep, bindingDep),
);
return { txSkeleton, mintEvent };
}
Expand Down Expand Up @@ -139,7 +139,7 @@ export async function collectCell(ckbAddress: string, neededCapacity: BI) {
export async function collectTypeCell(
ckbAddress: string,
type: Script | undefined,
total: number
total: number,
) {
const fromScript = helpers.parseAddress(ckbAddress, {
config: lumosConfig,
Expand All @@ -162,7 +162,7 @@ export async function collectTypeCell(
export async function listTypeCells(
ckbAddress: string,
type: Script | undefined,
maxTotal: number
maxTotal: number,
) {
const fromScript = helpers.parseAddress(ckbAddress, {
config: lumosConfig,
Expand Down Expand Up @@ -211,7 +211,7 @@ export function buildDeadLock(): Script {

export function computeTransactionHash(rawTransaction: Transaction) {
const transactionSerialized = bytes.hexify(
blockchain.RawTransaction.pack(rawTransaction)
blockchain.RawTransaction.pack(rawTransaction),
);
const rawTXHash = utils.ckbHash(transactionSerialized);
return rawTXHash;
Expand Down
Loading

0 comments on commit 7f0d669

Please sign in to comment.