added event collector

This commit is contained in:
Ren Amamiya 2023-04-10 15:50:38 +07:00
parent 213d7514d2
commit fabc0e6cc2
9 changed files with 232 additions and 57 deletions

View File

@ -0,0 +1,12 @@
-- CreateTable
CREATE TABLE "Chat" (
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
"pubkey" TEXT NOT NULL,
"createdAt" INTEGER NOT NULL
);
-- CreateIndex
CREATE UNIQUE INDEX "Chat_pubkey_key" ON "Chat"("pubkey");
-- CreateIndex
CREATE INDEX "Chat_pubkey_idx" ON "Chat"("pubkey");

View File

@ -0,0 +1,35 @@
/*
Warnings:
- Added the required column `accountId` to the `Chat` table without a default value. This is not possible if the table is not empty.
- Added the required column `accountId` to the `Channel` table without a default value. This is not possible if the table is not empty.
*/
-- RedefineTables
PRAGMA foreign_keys=OFF;
CREATE TABLE "new_Chat" (
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
"pubkey" TEXT NOT NULL,
"createdAt" INTEGER NOT NULL,
"accountId" INTEGER NOT NULL,
CONSTRAINT "Chat_accountId_fkey" FOREIGN KEY ("accountId") REFERENCES "Account" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
);
INSERT INTO "new_Chat" ("createdAt", "id", "pubkey") SELECT "createdAt", "id", "pubkey" FROM "Chat";
DROP TABLE "Chat";
ALTER TABLE "new_Chat" RENAME TO "Chat";
CREATE UNIQUE INDEX "Chat_pubkey_key" ON "Chat"("pubkey");
CREATE INDEX "Chat_pubkey_idx" ON "Chat"("pubkey");
CREATE TABLE "new_Channel" (
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
"eventId" TEXT NOT NULL,
"content" TEXT NOT NULL,
"accountId" INTEGER NOT NULL,
CONSTRAINT "Channel_accountId_fkey" FOREIGN KEY ("accountId") REFERENCES "Account" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
);
INSERT INTO "new_Channel" ("content", "eventId", "id") SELECT "content", "eventId", "id" FROM "Channel";
DROP TABLE "Channel";
ALTER TABLE "new_Channel" RENAME TO "Channel";
CREATE UNIQUE INDEX "Channel_eventId_key" ON "Channel"("eventId");
CREATE INDEX "Channel_eventId_idx" ON "Channel"("eventId");
PRAGMA foreign_key_check;
PRAGMA foreign_keys=ON;

View File

@ -21,6 +21,8 @@ model Account {
plebs Pleb[]
messages Message[]
notes Note[]
chats Chat[]
channels Channel[]
@@index([pubkey])
}
@ -66,11 +68,25 @@ model Message {
@@index([pubkey, createdAt])
}
model Chat {
id Int @id @default(autoincrement())
pubkey String @unique
createdAt Int
Account Account @relation(fields: [accountId], references: [id])
accountId Int
@@index([pubkey])
}
model Channel {
id Int @id @default(autoincrement())
eventId String @unique
content String
Account Account @relation(fields: [accountId], references: [id])
accountId Int
@@index([eventId])
}

View File

@ -81,10 +81,23 @@ struct GetLatestNoteData {
date: i32,
}
#[derive(Deserialize, Type)]
struct CreateChatData {
pubkey: String,
created_at: i32,
account_id: i32,
}
#[derive(Deserialize, Type)]
struct GetChatData {
account_id: i32,
}
#[derive(Deserialize, Type)]
struct CreateChannelData {
event_id: String,
content: String,
account_id: i32,
}
#[tauri::command]
@ -225,7 +238,45 @@ async fn count_total_notes(db: DbState<'_>) -> Result<i64, ()> {
#[specta::specta]
async fn create_channel(db: DbState<'_>, data: CreateChannelData) -> Result<channel::Data, ()> {
db.channel()
.create(data.event_id, data.content, vec![])
.upsert(
channel::event_id::equals(data.event_id.clone()),
channel::create(
data.event_id,
data.content,
account::id::equals(data.account_id),
vec![],
),
vec![],
)
.exec()
.await
.map_err(|_| ())
}
#[tauri::command]
#[specta::specta]
async fn create_chat(db: DbState<'_>, data: CreateChatData) -> Result<chat::Data, ()> {
db.chat()
.upsert(
chat::pubkey::equals(data.pubkey.clone()),
chat::create(
data.pubkey,
data.created_at,
account::id::equals(data.account_id),
vec![],
),
vec![],
)
.exec()
.await
.map_err(|_| ())
}
#[tauri::command]
#[specta::specta]
async fn get_chats(db: DbState<'_>, data: GetChatData) -> Result<Vec<chat::Data>, ()> {
db.chat()
.find_many(vec![chat::account_id::equals(data.account_id)])
.exec()
.await
.map_err(|_| ())
@ -247,7 +298,9 @@ async fn main() {
get_notes,
get_latest_notes,
get_note_by_id,
create_channel
create_channel,
create_chat,
get_chats
],
"../src/utils/bindings.ts",
)
@ -290,7 +343,9 @@ async fn main() {
get_latest_notes,
get_note_by_id,
count_total_notes,
create_channel
create_channel,
create_chat,
get_chats
])
.manage(Arc::new(db))
.run(tauri::generate_context!())

View File

@ -4,7 +4,7 @@ const AppActions = dynamic(() => import('@components/appHeader/actions'), {
ssr: false,
});
const NoteConnector = dynamic(() => import('@components/note/connector'), {
const EventCollector = dynamic(() => import('@components/eventCollector'), {
ssr: false,
});
@ -15,7 +15,7 @@ export default function AppHeader() {
<div data-tauri-drag-region className="flex h-full w-full items-center justify-between">
<div className="flex h-full items-center divide-x divide-zinc-900 px-4 pt-px"></div>
<div>
<NoteConnector />
<EventCollector />
</div>
</div>
</div>

View File

@ -1,23 +1,21 @@
import { ChatListItem } from '@components/chats/chatListItem';
import { ChatModal } from '@components/chats/chatModal';
import { ImageWithFallback } from '@components/imageWithFallback';
import { RelayContext } from '@components/relaysProvider';
import { activeAccountAtom } from '@stores/account';
import { DEFAULT_AVATAR } from '@stores/constants';
import { useAtomValue } from 'jotai';
import { useRouter } from 'next/router';
import { useContext, useEffect, useState } from 'react';
import { useEffect, useState } from 'react';
export default function ChatList() {
const [pool, relays]: any = useContext(RelayContext);
const router = useRouter();
const activeAccount: any = useAtomValue(activeAccountAtom);
const accountProfile = JSON.parse(activeAccount.metadata);
const [list, setList] = useState(new Set());
const [list, setList] = useState([]);
const openSelfChat = () => {
router.push({
@ -27,26 +25,15 @@ export default function ChatList() {
};
useEffect(() => {
const unsubscribe = pool.subscribe(
[
{
kinds: [4],
'#p': [activeAccount.pubkey],
since: 0,
},
],
relays,
(event: any) => {
if (event.pubkey !== activeAccount.pubkey) {
setList((list) => new Set(list).add(event.pubkey));
}
}
);
return () => {
unsubscribe;
const fetchChats = async () => {
const { getChats } = await import('@utils/bindings');
return await getChats({ account_id: activeAccount.id });
};
}, [pool, relays, activeAccount.pubkey]);
fetchChats()
.then((res) => setList(res))
.catch(console.error);
}, [activeAccount.id]);
return (
<div className="flex flex-col gap-px">
@ -68,8 +55,8 @@ export default function ChatList() {
</h5>
</div>
</div>
{[...list].map((item: string, index) => (
<ChatListItem key={index} pubkey={item} />
{list.map((item) => (
<ChatListItem key={item.id} pubkey={item.pubkey} />
))}
<ChatModal />
</div>

View File

@ -11,7 +11,7 @@ import { appWindow, getCurrent } from '@tauri-apps/api/window';
import { useSetAtom } from 'jotai';
import { useCallback, useContext, useEffect, useRef, useState } from 'react';
export default function NoteConnector() {
export default function EventCollector() {
const [pool, relays]: any = useContext(RelayContext);
const setLastLoginAtom = useSetAtom(lastLoginAtom);
@ -24,6 +24,9 @@ export default function NoteConnector() {
const subscribe = useCallback(async () => {
const { createNote } = await import('@utils/bindings');
const { createChat } = await import('@utils/bindings');
const { createChannel } = await import('@utils/bindings');
const activeAccount = JSON.parse(localStorage.getItem('activeAccount'));
const follows = JSON.parse(localStorage.getItem('activeAccountFollows'));
@ -34,29 +37,47 @@ export default function NoteConnector() {
authors: pubkeyArray(follows),
since: dateToUnix(now.current),
},
{
kinds: [4],
'#p': [activeAccount.pubkey],
since: 0,
},
{
kinds: [40],
since: 0,
},
],
relays,
(event) => {
const parentID = getParentID(event.tags, event.id);
// insert event to local database
createNote({
event_id: event.id,
pubkey: event.pubkey,
kind: event.kind,
tags: JSON.stringify(event.tags),
content: event.content,
parent_id: parentID,
parent_comment_id: '',
created_at: event.created_at,
account_id: activeAccount.id,
})
.then(() =>
// notify user reload to get newer note
setHasNewerNote(true)
)
.catch(console.error);
},
10000
if (event.kind === 1) {
const parentID = getParentID(event.tags, event.id);
// insert event to local database
createNote({
event_id: event.id,
pubkey: event.pubkey,
kind: event.kind,
tags: JSON.stringify(event.tags),
content: event.content,
parent_id: parentID,
parent_comment_id: '',
created_at: event.created_at,
account_id: activeAccount.id,
})
.then(() =>
// notify user reload to get newer note
setHasNewerNote(true)
)
.catch(console.error);
} else if (event.kind === 4) {
if (event.pubkey !== activeAccount.pubkey) {
createChat({ pubkey: event.pubkey, created_at: event.created_at, account_id: activeAccount.id });
}
} else if (event.kind === 40) {
createChannel({ event_id: event.id, content: event.content, account_id: activeAccount.id });
} else {
console.error;
}
}
);
}, [pool, relays, setHasNewerNote]);

View File

@ -1,10 +1,48 @@
import BaseLayout from '@layouts/base';
import WithSidebarLayout from '@layouts/withSidebar';
import { JSXElementConstructor, ReactElement, ReactFragment, ReactPortal } from 'react';
import { RelayContext } from '@components/relaysProvider';
import {
JSXElementConstructor,
ReactElement,
ReactFragment,
ReactPortal,
useContext,
useEffect,
useState,
} from 'react';
export default function Page() {
return <></>;
const [pool, relays]: any = useContext(RelayContext);
const [list, setList] = useState([]);
useEffect(() => {
const unsubscribe = pool.subscribe(
[
{
kinds: [40],
since: 0,
},
],
relays,
(event: any) => {
setList((list) => [event, ...list]);
}
);
return () => {
unsubscribe;
};
}, [pool, relays]);
return (
<div className="h-full w-full overflow-y-auto">
{list.map((channel) => (
<div key={channel.id}>{channel.content}</div>
))}
</div>
);
}
Page.getLayout = function getLayout(

View File

@ -48,6 +48,14 @@ export function createChannel(data: CreateChannelData) {
return invoke<Channel>('create_channel', { data });
}
export function createChat(data: CreateChatData) {
return invoke<Chat>('create_chat', { data });
}
export function getChats(data: GetChatData) {
return invoke<Chat[]>('get_chats', { data });
}
export type CreateNoteData = {
event_id: string;
pubkey: string;
@ -59,10 +67,7 @@ export type CreateNoteData = {
created_at: number;
account_id: number;
};
export type CreateChannelData = { event_id: string; content: string };
export type CreatePlebData = { pleb_id: string; pubkey: string; kind: number; metadata: string; account_id: number };
export type GetNoteByIdData = { event_id: string };
export type Pleb = { id: number; plebId: string; pubkey: string; kind: number; metadata: string; accountId: number };
export type Note = {
id: number;
eventId: string;
@ -75,10 +80,16 @@ export type Note = {
createdAt: number;
accountId: number;
};
export type CreateChatData = { pubkey: string; created_at: number; account_id: number };
export type GetNoteByIdData = { event_id: string };
export type Chat = { id: number; pubkey: string; createdAt: number; accountId: number };
export type Account = { id: number; pubkey: string; privkey: string; active: boolean; metadata: string };
export type Channel = { id: number; eventId: string; content: string };
export type GetChatData = { account_id: number };
export type CreateChannelData = { event_id: string; content: string; account_id: number };
export type GetPlebPubkeyData = { pubkey: string };
export type Channel = { id: number; eventId: string; content: string; accountId: number };
export type GetPlebData = { account_id: number };
export type CreateAccountData = { pubkey: string; privkey: string; metadata: string };
export type GetLatestNoteData = { date: number };
export type Pleb = { id: number; plebId: string; pubkey: string; kind: number; metadata: string; accountId: number };
export type GetNoteData = { date: number; limit: number; offset: number };