mirror of
https://github.com/BlowaterNostr/blowater.git
synced 2024-10-18 07:33:22 +00:00
parent
05270b9bc7
commit
a3d213538d
@ -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);
|
||||
|
@ -354,6 +354,7 @@ export function AppComponent(props: {
|
||||
myPublicKey={myAccountCtx.publicKey}
|
||||
messages={socialPosts}
|
||||
rightPanelModel={app.model.rightPanelModel}
|
||||
db={app.database}
|
||||
eventEmitter={app.eventBus}
|
||||
/>
|
||||
</div>
|
||||
|
@ -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 : "",
|
||||
|
@ -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}]`;
|
||||
|
@ -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,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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>
|
||||
|
@ -69,7 +69,7 @@ Deno.test("inline parse", async (t) => {
|
||||
input: `nostr:npub17dxnfw2vrhgtk4fgqdmpuqxv05u9raau3w0shay7msmr0dzs4m7s6ng4ylログボ`,
|
||||
output: [{
|
||||
type: "npub",
|
||||
start: 0,
|
||||
start: 6,
|
||||
end: 68,
|
||||
}],
|
||||
},
|
||||
|
@ -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");
|
||||
|
10
deno.lock
10
deno.lock
@ -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",
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user