Render Pubkey in DM (#11)

Co-authored-by: Foodstr <foodstr@proton.me>
This commit is contained in:
BlowaterNostr 2023-07-04 14:37:14 +08:00 committed by GitHub
parent 05270b9bc7
commit a3d213538d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 70 additions and 23 deletions

View File

@ -1,7 +1,6 @@
import { DB } from "https://deno.land/x/sqlite@v3.7.2/mod.ts";
import { SQL } from "./types.ts";
const query: SQL = `SELECT DISTINCT(pubkey) From stats`;
const query = `SELECT DISTINCT(pubkey) From stats`;
const db = new DB("stats.sqlite");
const res = db.query(query);

View File

@ -354,6 +354,7 @@ export function AppComponent(props: {
myPublicKey={myAccountCtx.publicKey}
messages={socialPosts}
rightPanelModel={app.model.rightPanelModel}
db={app.database}
eventEmitter={app.eventBus}
/>
</div>

View File

@ -109,7 +109,7 @@ export async function* UI_Interaction_Update(
const pubkey = PublicKey.FromString(event.text);
if (pubkey instanceof PublicKey) {
await profileSyncer.add(pubkey.hex);
const profile = getProfileEvent(app.database, pubkey.hex);
const profile = getProfileEvent(app.database, pubkey);
app.model.dm.search.searchResults = [{
pubkey: pubkey,
profile: profile?.content,
@ -430,7 +430,7 @@ export async function* Database_Update(
if (model.dm.search.searchResults.length > 0) {
const previous = model.dm.search.searchResults;
model.dm.search.searchResults = previous.map((profile) => {
const profileEvent = getProfileEvent(database, profile.pubkey.hex);
const profileEvent = getProfileEvent(database, profile.pubkey);
return {
pubkey: profile.pubkey,
profile: profileEvent?.content,
@ -439,14 +439,19 @@ export async function* Database_Update(
}
// my profile update
if (ctx && e.pubkey == ctx.publicKey.hex) {
const newProfile = getProfileEvent(database, ctx.publicKey.hex);
const newProfile = getProfileEvent(database, ctx.publicKey);
if (newProfile == undefined) {
throw new Error("impossible");
}
model.myProfile = newProfile.content;
}
} else if (e.kind == NostrKind.DIRECT_MESSAGE) {
const author = getProfileEvent(database, e.pubkey);
const pubkey = PublicKey.FromHex(e.pubkey);
if (pubkey instanceof Error) {
console.error(pubkey);
continue;
}
const author = getProfileEvent(database, pubkey);
if (e.pubkey != ctx.publicKey.hex) {
notify(
author?.content.name ? author.content.name : "",

View File

@ -3,7 +3,7 @@ import { DividerBackgroundColor, PlaceholderColor, PrimaryTextColor } from "../s
export const CenterClass = "flex items-center justify-center";
export const NoOutlineClass = "focus:outline-none focus-visible:outline-none";
export const inputBorderClass = `border-[2px] border-[${DividerBackgroundColor}]`;
export const DividerClass = `h-[0.0625rem] bg-[${DividerBackgroundColor}] mt-[1.5rem] mb-[1.5rem]`;
export const DividerClass = `h-[0.0625rem] bg-[${DividerBackgroundColor}] my-[1.5rem]`;
export const LinearGradientsClass = "bg-gradient-to-r from-[#FF762C] via-[#FF3A5E] to-[#FF01A9]";
export const InputClass =
`w-full px-[1rem] py-[0.75rem] rounded-lg resize-y bg-transparent ${NoOutlineClass} ${inputBorderClass} text-[${PrimaryTextColor}] placeholder-[${PlaceholderColor}]`;

View File

@ -45,7 +45,7 @@ export function DirectMessageContainer(props: DirectMessageContainerProps) {
for (const [v, editor] of props.editors.entries()) {
if (v == currentConversation.hex) {
currentEditorModel = editor;
const profile = getProfileEvent(props.db, currentConversation.hex);
const profile = getProfileEvent(props.db, currentConversation);
currentEditorModel.target.receiver = {
pubkey: currentConversation,
name: profile?.content.name,
@ -106,6 +106,7 @@ export function DirectMessageContainer(props: DirectMessageContainerProps) {
eventEmitter: props.eventEmitter,
editorModel: currentEditorModel,
focusedContent: focusedContent,
db: props.db,
});
}

View File

@ -5,7 +5,7 @@ import { Editor, EditorEvent, EditorModel } from "./editor.tsx";
import { CloseIcon, LeftArrowIcon, ReplyIcon } from "./icons/mod.tsx";
import { Avatar } from "./components/avatar.tsx";
import { IconButtonClass } from "./components/tw.ts";
import { DividerClass, IconButtonClass } from "./components/tw.ts";
import { sleep } from "https://raw.githubusercontent.com/BlowaterNostr/csp/master/csp.ts";
import { EventEmitter } from "../event-bus.ts";
@ -23,10 +23,12 @@ import {
NostrKind,
} from "https://raw.githubusercontent.com/BlowaterNostr/nostr.ts/main/nostr.ts";
import { PinContact, UnpinContact } from "../nostr.ts";
import { ProfileData } from "../features/profile.ts";
import { getProfileEvent, ProfileData } from "../features/profile.ts";
import { MessageThread } from "./dm.tsx";
import { UserDetail } from "./user-detail.tsx";
import { MessageThreadPanel } from "./message-thread-panel.tsx";
import { Database } from "../database.ts";
import { DividerBackgroundColor, PrimaryBackgroundColor, PrimaryTextColor } from "./style/colors.ts";
interface DirectMessagePanelProps {
myPublicKey: PublicKey;
@ -45,6 +47,7 @@ interface DirectMessagePanelProps {
rightPanelModel: RightPanelModel;
db: Database;
eventEmitter: EventEmitter<
EditorEvent | DirectMessagePanelUpdate | PinContact | UnpinContact
>;
@ -91,6 +94,7 @@ export function MessagePanel(props: DirectMessagePanelProps) {
eventEmitter={props.eventEmitter}
messages={[props.focusedContent.data.root, ...props.focusedContent.data.replies]}
myPublicKey={props.myPublicKey}
db={props.db}
editorModel={props.focusedContent.editor}
/>
);
@ -127,6 +131,7 @@ export function MessagePanel(props: DirectMessagePanelProps) {
myPublicKey={props.myPublicKey}
threads={props.messages}
eventEmitter={props.eventEmitter}
db={props.db}
/>
}
{
@ -170,6 +175,7 @@ export function MessagePanel(props: DirectMessagePanelProps) {
interface MessageListProps {
myPublicKey: PublicKey;
threads: MessageThread[];
db: Database;
eventEmitter?: EventEmitter<DirectMessagePanelUpdate>;
}
@ -241,6 +247,7 @@ export class MessageList extends Component<MessageListProps, MessageListState> {
}),
myPublicKey: this.props.myPublicKey,
eventEmitter: this.props.eventEmitter,
db: this.props.db,
}),
);
}
@ -292,6 +299,7 @@ function MessageBoxGroup(props: {
replyCount: number;
}[];
myPublicKey: PublicKey;
db: Database;
eventEmitter?: EventEmitter<DirectMessagePanelUpdate | ViewUserDetail>;
}) {
// const t = Date.now();
@ -333,7 +341,7 @@ function MessageBoxGroup(props: {
<pre
class={tw`text-[#DCDDDE] whitespace-pre-wrap break-words font-roboto`}
>
{ParseMessageContent(msg.msg)}
{ParseMessageContent(msg.msg, props.db)}
</pre>
{msg.replyCount > 0
? (
@ -419,7 +427,7 @@ export function NameAndTime(message: ChatMessage, index: number, myPublicKey: Pu
}
}
export function ParseMessageContent(message: ChatMessage) {
export function ParseMessageContent(message: ChatMessage, db: Database) {
let vnode: VNode | VNode[];
if (isImage(message)) {
vnode = <img src={message.content} />;
@ -427,15 +435,20 @@ export function ParseMessageContent(message: ChatMessage) {
const items = Array.from(parseContent(message.content));
vnode = [<p>{message.content}</p>];
for (const item of items) {
const itemStr = message.content.slice(item.start, item.end + 1);
switch (item.type) {
case "url":
const url = message.content.slice(item.start, item.end + 1);
if (urlIsImage(url)) {
vnode.push(<img src={url} />);
if (urlIsImage(itemStr)) {
vnode.push(<img src={itemStr} />);
}
break;
case "npub":
// todo
const pubkey = PublicKey.FromBech32(itemStr);
const profile = getProfileEvent(db, pubkey);
console.log(profile);
if (profile) {
vnode.push(ProfileCard(profile.content, profile.pubkey));
}
break;
case "tag":
// todo
@ -443,10 +456,22 @@ export function ParseMessageContent(message: ChatMessage) {
}
}
}
return vnode;
}
function ProfileCard(profile: ProfileData, pubkey: string) {
return (
<div class={tw`px-4 py-2 border-2 border-[${PrimaryTextColor}4D] rounded-lg`}>
<div class={tw`flex`}>
<Avatar class={tw`w-10 h-10`} picture={profile.picture}></Avatar>
<p class={tw`text-[1.2rem] font-blod leading-10 truncate ml-2`}>{profile.name || pubkey}</p>
</div>
<div class={tw`${DividerClass} my-[0.5rem]`}></div>
<p class={tw`text-[0.8rem]`}>{profile.about}</p>
</div>
);
}
type RightPanelProps = {
eventEmitter: EventEmitter<DirectMessagePanelUpdate>;
rightPanelModel: RightPanelModel;

View File

@ -11,11 +11,13 @@ import {
import { PublicKey } from "https://raw.githubusercontent.com/BlowaterNostr/nostr.ts/main/key.ts";
import { ChatMessage, groupContinuousMessages } from "./message.ts";
import { Editor, EditorEvent, EditorModel } from "./editor.tsx";
import { Database } from "../database.ts";
interface MessageThreadProps {
eventEmitter: EventEmitter<DirectMessagePanelUpdate | EditorEvent>;
messages: ChatMessage[];
myPublicKey: PublicKey;
db: Database;
editorModel: EditorModel;
}
@ -36,6 +38,7 @@ export function MessageThreadPanel(props: MessageThreadProps) {
<MessageThreadList
myPublicKey={props.myPublicKey}
messages={props.messages}
db={props.db}
/>
</div>
@ -52,6 +55,7 @@ export function MessageThreadPanel(props: MessageThreadProps) {
function MessageThreadList(props: {
myPublicKey: PublicKey;
messages: ChatMessage[];
db: Database;
}) {
let groups = groupContinuousMessages(props.messages, (pre, cur) => {
const sameAuthor = pre.event.pubkey == cur.event.pubkey;
@ -64,6 +68,7 @@ function MessageThreadList(props: {
<MessageThreadBoxGroup
messages={group}
myPublicKey={props.myPublicKey}
db={props.db}
/>,
);
}
@ -79,6 +84,7 @@ function MessageThreadList(props: {
function MessageThreadBoxGroup(props: {
messages: ChatMessage[];
myPublicKey: PublicKey;
db: Database;
}) {
const vnode = (
<ul class={tw`py-2`}>
@ -96,7 +102,7 @@ function MessageThreadBoxGroup(props: {
<pre
class={tw`text-[#DCDDDE] whitespace-pre-wrap break-words font-roboto`}
>
{ParseMessageContent(msg)}
{ParseMessageContent(msg, props.db)}
</pre>
</div>
</li>

View File

@ -69,7 +69,7 @@ Deno.test("inline parse", async (t) => {
input: `nostr:npub17dxnfw2vrhgtk4fgqdmpuqxv05u9raau3w0shay7msmr0dzs4m7s6ng4ylログボ`,
output: [{
type: "npub",
start: 0,
start: 6,
end: 68,
}],
},

View File

@ -7,7 +7,7 @@ export function* parseContent(content: string) {
yield* match(/https?:\/\/[^\s]+/g, content, "url");
// npubs
yield* match(/nostr:npub[0-9a-z]{59}/g, content, "npub");
yield* match(/npub[0-9a-z]{59}/g, content, "npub");
// tags
yield* match(/#\[[0-9]+\]/g, content, "tag");

View File

@ -6,6 +6,16 @@
"https://deno.land/std@0.176.0/testing/_diff.ts": "1a3c044aedf77647d6cac86b798c6417603361b66b54c53331b312caeb447aea",
"https://deno.land/std@0.176.0/testing/_format.ts": "a69126e8a469009adf4cf2a50af889aca364c349797e63174884a52ff75cf4c7",
"https://deno.land/std@0.176.0/testing/asserts.ts": "984ab0bfb3faeed92ffaa3a6b06536c66811185328c5dd146257c702c41b01ab",
"https://deno.land/x/sqlite@v3.7.2/build/sqlite.d.ts": "d724a21a588a0e19ae46a3476349fe5d75e896735e7362ea8cf997ffad35d0f9",
"https://deno.land/x/sqlite@v3.7.2/build/sqlite.js": "c59f109f100c2bae0b9342f04e0d400583e2e3211d08bb71095177a4109ee5bf",
"https://deno.land/x/sqlite@v3.7.2/build/vfs.js": "08533cc78fb29b9d9bd62f6bb93e5ef333407013fed185776808f11223ba0e70",
"https://deno.land/x/sqlite@v3.7.2/mod.ts": "e09fc79d8065fe222578114b109b1fd60077bff1bb75448532077f784f4d6a83",
"https://deno.land/x/sqlite@v3.7.2/src/constants.ts": "90f3be047ec0a89bcb5d6fc30db121685fc82cb00b1c476124ff47a4b0472aa9",
"https://deno.land/x/sqlite@v3.7.2/src/db.ts": "62f3ad4d593c07e7c64fbbd97da6b4579ea695f8d11d52d899661e9d42a400fd",
"https://deno.land/x/sqlite@v3.7.2/src/error.ts": "f7a15cb00d7c3797da1aefee3cf86d23e0ae92e73f0ba3165496c3816ab9503a",
"https://deno.land/x/sqlite@v3.7.2/src/function.ts": "e4c83b8ec64bf88bafad2407376b0c6a3b54e777593c70336fb40d43a79865f2",
"https://deno.land/x/sqlite@v3.7.2/src/query.ts": "d58abda928f6582d77bad685ecf551b1be8a15e8e38403e293ec38522e030cad",
"https://deno.land/x/sqlite@v3.7.2/src/wasm.ts": "e79d0baa6e42423257fb3c7cc98091c54399254867e0f34a09b5bdef37bd9487",
"https://esm.sh/preact@10.11.3": "ad3c24796c4132c84b4b392f812228d53a0fd600fa64648f27aebd05ec09b24e",
"https://esm.sh/stable/preact@10.11.3/denonext/preact.mjs": "c828d9020ea26f07ba07b2f0a3ef97fd7ceb1c2a772c380ca05e2647ac8d3923",
"https://esm.sh/twind@0.16.16": "7f93bf4dc6102cef9a66689807b09f6bf35cbd8dd397b550fc97525c3e71e196",

View File

@ -72,9 +72,9 @@ export async function saveProfile(
pool.sendEvent(event);
}
export function getProfileEvent(db: Database, pubkey: string): ProfileEvent | undefined {
export function getProfileEvent(db: Database, pubkey: PublicKey): ProfileEvent | undefined {
const events = Array.from(db.filterEvents((e) => {
return e.kind === NostrKind.META_DATA && e.pubkey === pubkey;
return e.kind === NostrKind.META_DATA && e.pubkey === pubkey.hex;
}));
if (events.length == 0) {
return undefined;
@ -110,7 +110,7 @@ export function getProfiles(
): Map<string, /*pubkey*/ ProfileEvent | undefined> {
const contacts: Map<string, ProfileEvent | undefined> = new Map();
for (const key of pubkeys) {
const event = getProfileEvent(db, key);
const event = getProfileEvent(db, PublicKey.FromHex(key) as PublicKey);
contacts.set(key, event);
}
return contacts;