seen user db

This commit is contained in:
Alejandro Gomez 2023-01-15 02:46:13 +01:00
parent 5cc08aaace
commit 344e309d4e
No known key found for this signature in database
GPG Key ID: 4DF39E566658C817
8 changed files with 77 additions and 16 deletions

View File

@ -16,6 +16,8 @@
"@types/webscopeio__react-textarea-autocomplete": "^4.7.2", "@types/webscopeio__react-textarea-autocomplete": "^4.7.2",
"@webscopeio/react-textarea-autocomplete": "^4.9.2", "@webscopeio/react-textarea-autocomplete": "^4.9.2",
"bech32": "^2.0.0", "bech32": "^2.0.0",
"dexie": "^3.2.2",
"dexie-react-hooks": "^1.1.1",
"light-bolt11-decoder": "^2.1.0", "light-bolt11-decoder": "^2.1.0",
"qr-code-styling": "^1.6.0-rc.1", "qr-code-styling": "^1.6.0-rc.1",
"react": "^18.2.0", "react": "^18.2.0",

16
src/db.ts Normal file
View File

@ -0,0 +1,16 @@
import Dexie, { Table } from 'dexie';
import type { User } from './nostr/types';
export class MySubClassedDexie extends Dexie {
users!: Table<User>;
constructor() {
super('snortDB');
this.version(1).stores({
users: '++pubkey, name, display_name, about, nip05' // Primary key and indexed props
});
}
}
export const db = new MySubClassedDexie();

View File

@ -1,4 +1,5 @@
import { Component } from "react"; import { useSelector } from "react-redux";
import { useLiveQuery } from "dexie-react-hooks";
import ReactTextareaAutocomplete from "@webscopeio/react-textarea-autocomplete"; import ReactTextareaAutocomplete from "@webscopeio/react-textarea-autocomplete";
@ -11,10 +12,11 @@ import Nostrich from "../nostrich.jpg";
// @ts-expect-error // @ts-expect-error
import { hexToBech32 } from "../Util"; import { hexToBech32 } from "../Util";
import type { User } from "../nostr/types"; import type { User } from "../nostr/types";
import { db } from "../db";
function searchUsers(query: string, users: Record<string, User>) { function searchUsers(query: string, users: User[]) {
const q = query.toLowerCase() const q = query.toLowerCase()
return Object.values(users).filter(({ name, display_name, about, nip05 }) => { return users.filter(({ name, display_name, about, nip05 }: User) => {
return name?.toLowerCase().includes(q) return name?.toLowerCase().includes(q)
|| display_name?.toLowerCase().includes(q) || display_name?.toLowerCase().includes(q)
|| about?.toLowerCase().includes(q) || about?.toLowerCase().includes(q)
@ -37,29 +39,39 @@ const UserItem = ({ pubkey, display_name, picture, nip05, ...rest }: User) => {
) )
} }
export default class Textarea extends Component { function normalizeUser({ pubkey, about, nip05, name, display_name }: User) {
render() { return { pubkey, about, nip05, name, display_name }
}
const Textarea = ({ onChange, ...rest }: any) => {
// @ts-expect-error // @ts-expect-error
const { users, onChange, ...rest } = this.props const { users } = useSelector(s => s.users)
const dbUsers = useLiveQuery(
() => db.users.toArray().then(usrs => {
return usrs.reduce((acc, usr) => {
return { ...acc, [usr.pubkey]: normalizeUser(usr)}
}, {})
})
)
const cachedUsers = dbUsers ? dbUsers : {}
const allUsers: User[] = Object.values({...cachedUsers, ...users})
return ( return (
<ReactTextareaAutocomplete <ReactTextareaAutocomplete
{...rest} {...rest}
loadingComponent={() => <span>Loading....</span>} loadingComponent={() => <span>Loading....</span>}
placeholder="Say something!" placeholder="Say something!"
ref={rta => {
// @ts-expect-error
this.rta = rta;
}}
onChange={onChange} onChange={onChange}
trigger={{ trigger={{
"@": { "@": {
afterWhitespace: true, afterWhitespace: true,
dataProvider: token => searchUsers(token, users), dataProvider: token => dbUsers ? searchUsers(token, allUsers) : [],
component: (props: any) => <UserItem {...props.entity} />, component: (props: any) => <UserItem {...props.entity} />,
output: (item: any) => `@${hexToBech32("npub", item.pubkey)}` output: (item: any) => `@${hexToBech32("npub", item.pubkey)}`
} }
}} }}
/> />
) )
}
} }
export default Textarea

View File

@ -4,6 +4,7 @@ import EventKind from "../nostr/EventKind";
import { Subscriptions } from "../nostr/Subscriptions"; import { Subscriptions } from "../nostr/Subscriptions";
import { addDirectMessage, addNotifications, setFollows, setRelays } from "../state/Login"; import { addDirectMessage, addNotifications, setFollows, setRelays } from "../state/Login";
import { setUserData } from "../state/Users"; import { setUserData } from "../state/Users";
import { db } from "../db";
import useSubscription from "./Subscription"; import useSubscription from "./Subscription";
import { mapEventToProfile } from "./UsersFeed"; import { mapEventToProfile } from "./UsersFeed";
@ -41,7 +42,8 @@ export default function useLoginFeed() {
useEffect(() => { useEffect(() => {
let contactList = notes.filter(a => a.kind === EventKind.ContactList); let contactList = notes.filter(a => a.kind === EventKind.ContactList);
let notifications = notes.filter(a => a.kind === EventKind.TextNote); let notifications = notes.filter(a => a.kind === EventKind.TextNote);
let metadata = notes.filter(a => a.kind === EventKind.SetMetadata).map(a => mapEventToProfile(a)); let metadata = notes.filter(a => a.kind === EventKind.SetMetadata)
let profiles = metadata.map(a => mapEventToProfile(a));
let dms = notes.filter(a => a.kind === EventKind.DirectMessage); let dms = notes.filter(a => a.kind === EventKind.DirectMessage);
for (let cl of contactList) { for (let cl of contactList) {
@ -60,7 +62,11 @@ export default function useLoginFeed() {
} }
} }
dispatch(addNotifications(notifications)); dispatch(addNotifications(notifications));
dispatch(setUserData(metadata)); dispatch(setUserData(profiles));
const userMetadata = metadata.map(ev => {
return {...JSON.parse(ev.content), pubkey: ev.pubkey }
})
db.users.bulkPut(metadata);
dispatch(addDirectMessage(dms)); dispatch(addDirectMessage(dms));
}, [notes]); }, [notes]);
} }

View File

@ -2,6 +2,7 @@ import { useEffect, useMemo } from "react";
import { useDispatch, useSelector } from "react-redux"; import { useDispatch, useSelector } from "react-redux";
import { ProfileCacheExpire } from "../Const"; import { ProfileCacheExpire } from "../Const";
import EventKind from "../nostr/EventKind"; import EventKind from "../nostr/EventKind";
import { db } from "../db";
import { Subscriptions } from "../nostr/Subscriptions"; import { Subscriptions } from "../nostr/Subscriptions";
import { setUserData } from "../state/Users"; import { setUserData } from "../state/Users";
import useSubscription from "./Subscription"; import useSubscription from "./Subscription";
@ -34,7 +35,12 @@ export default function useUsersCache() {
const results = useSubscription(sub); const results = useSubscription(sub);
useEffect(() => { useEffect(() => {
dispatch(setUserData(results.notes.map(a => mapEventToProfile(a)))); const userData = results.notes.map(a => mapEventToProfile(a));
dispatch(setUserData(userData));
const profiles = results.notes.map(ev => {
return {...JSON.parse(ev.content), pubkey: ev.pubkey }
});
db.users.bulkPut(profiles);
}, [results]); }, [results]);
return results; return results;

View File

@ -1,4 +1,4 @@
export type User = { export interface User {
name?: string name?: string
about?: string about?: string
display_name?: string display_name?: string

View File

@ -1,5 +1,6 @@
import { createSlice } from '@reduxjs/toolkit' import { createSlice } from '@reduxjs/toolkit'
import { ProfileCacheExpire } from '../Const'; import { ProfileCacheExpire } from '../Const';
import { db } from '../db';
const UsersSlice = createSlice({ const UsersSlice = createSlice({
name: "Users", name: "Users",
@ -67,6 +68,14 @@ const UsersSlice = createSlice({
}; };
} }
state.users[x.pubkey] = x; state.users[x.pubkey] = x;
db.users.put({
pubkey: x.pubkey,
name: x.name,
display_name: x.display_name,
nip05: x.nip05,
picture: x.picture,
about: x.about,
})
window.localStorage.setItem(`user:${x.pubkey}`, JSON.stringify(x)); window.localStorage.setItem(`user:${x.pubkey}`, JSON.stringify(x));
state.users = { state.users = {

View File

@ -3704,6 +3704,16 @@ detective@^5.2.1:
defined "^1.0.0" defined "^1.0.0"
minimist "^1.2.6" minimist "^1.2.6"
dexie-react-hooks@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/dexie-react-hooks/-/dexie-react-hooks-1.1.1.tgz#ff405cc89e5d899ddbac5e40d593f83f9a74106a"
integrity sha512-Cam5JP6PxHN564RvWEoe8cqLhosW0O4CAZ9XEVYeGHJBa6KEJlOpd9CUpV3kmU9dm2MrW97/lk7qkf1xpij7gA==
dexie@^3.2.2:
version "3.2.2"
resolved "https://registry.yarnpkg.com/dexie/-/dexie-3.2.2.tgz#fa6f2a3c0d6ed0766f8d97a03720056f88fe0e01"
integrity sha512-q5dC3HPmir2DERlX+toCBbHQXW5MsyrFqPFcovkH9N2S/UW/H3H5AWAB6iEOExeraAu+j+zRDG+zg/D7YhH0qg==
didyoumean@^1.2.2: didyoumean@^1.2.2:
version "1.2.2" version "1.2.2"
resolved "https://registry.yarnpkg.com/didyoumean/-/didyoumean-1.2.2.tgz#989346ffe9e839b4555ecf5666edea0d3e8ad037" resolved "https://registry.yarnpkg.com/didyoumean/-/didyoumean-1.2.2.tgz#989346ffe9e839b4555ecf5666edea0d3e8ad037"