Skip to content

Commit

Permalink
Merge pull request #55 from Giszmo/louis/construct-in-group
Browse files Browse the repository at this point in the history
  • Loading branch information
Giszmo authored Nov 27, 2022
2 parents c9a37c9 + e1b9eb0 commit 1b3ae0f
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 66 deletions.
4 changes: 3 additions & 1 deletion src/components/Profile.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import { base } from '$app/paths';
import { db } from '../db';
import type { IProfile } from '../db';
import { activeProfile } from '../stores';
import { activeProfile, cProfiles } from '../stores';
import AvatarImage from './AvatarImage.svelte';
import { createEventDispatcher } from 'svelte';
Expand All @@ -18,6 +18,8 @@
key: 'activePubkey',
value: profile.pubkey
});
$cProfiles.updateDegrees();
};
const showPubkey = () => {
goto(`${base}/${profile?.pubkey}`);
Expand Down
67 changes: 8 additions & 59 deletions src/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import type { IProfile, IEvent } from './db';
import { snooze, yieldMicrotask } from './utils/sync';
import { chunks, filterMap, filterTF, forEach } from './utils/array';
import { deepCloneObj } from './utils/clone';
import { getDegreesForPubkeys } from './nostrHelper';

const itemKinds = [0, 1, 3, 4, 5, 7];
export class $Data {
Expand Down Expand Up @@ -238,71 +239,19 @@ export class $Data {

// of all the profiles we know, who is following whom?
// Map<pubkey, List<pubkey>>
const profileFollows = new Map(
(await db.events.where('kind').equals(3).toArray()).map((it) => [
it.pubkey,
it.tags.filter((it) => it.startsWith('p»')).map((it) => it.split('»', 3)[1])
])
);
const profiles = await db.profiles.toArray();
const allProfiles = await db.profiles.toArray();

// n-th degree follows' pubkeys
const follows: Array<Set<string>> = [];
// 0-th
follows[0] = new Set(
filterMap(
profiles,
(it) => it.degree === 0,
(it) => it.pubkey
)
const ownProfiles = filterMap(
allProfiles,
(it) => it.degree === 0,
(it) => it.pubkey
);

// n-th
let all = new Set<string>();
for (let i = 0; i < 10; i++) {
all = new Set(follows.flatMap((it) => [...it]));
if (all.size > 10000) {
console.log(`Exploring ${all.size} follows to the ${i}-th degree ...`);
break;
}
// 1st
const fArray = Array.from(follows[i])
.flatMap((it) => profileFollows.get(it)!)
.filter((it) => !all.has(it));
const newSet = new Set(fArray);
if (newSet.size == 0) {
console.log(
`Exploring ${all.size} follows to the ${i}-th degree. No more follows found in the ${
i + 1
}-th degree ...`
);
break;
}
follows[i + 1] = newSet;
}

profiles.forEach((profile) => {
if (all.has(profile.pubkey)) {
// we have a degree. Store it!
profile.degree = follows.findIndex((it) => it.has(profile.pubkey));
// remove pubkey from `all`, to later store all those profiles as missing
all.delete(profile.pubkey);
}
});
// for the remaining pubkeys, request profiles
all.forEach((pubkey) => {
if (pubkey) {
profiles.push((<IProfile>{
pubkey: pubkey,
missing: true,
degree: follows.findIndex((it) => it.has(pubkey))
}) as IProfile);
}
});
const profilesWithDegrees = await getDegreesForPubkeys(ownProfiles, allProfiles);

// Chunk the profiles into batches and run `bulkPut` on each chunk
// `bulkPut` is more efficient than regular `put`
const batchedProfiles = chunks(profiles, 250);
const batchedProfiles = chunks(profilesWithDegrees, 250);
for (const chunk of batchedProfiles) {
db.profiles.bulkPut(chunk);
}
Expand Down
49 changes: 48 additions & 1 deletion src/nostrHelper.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { db } from './db';
import { db, type IProfile } from './db';
import type { IEvent } from './db';
// import { getPublicKey } from 'nostr-tools'
// import {getEventHash, signEvent } from 'nostr-tools/event.js'
import { getPublicKey } from './lib/nostr-tools';
import { getEventHash, signEvent } from './lib/nostr-tools/event.js';
import { chunks, filterMap, filterTF, forEach } from './utils/array';

export const sendPersistEvent = async (kind, tags, content, privkey) => {
let pubkey = getPublicKey(privkey);
Expand All @@ -24,3 +25,49 @@ export const sendPersistEvent = async (kind, tags, content, privkey) => {
e.sig = await signEvent(event, privkey);
db.events.add(e);
};

export const getDegreesForPubkeys = async (pubkeys: string[], profiles: IProfile[]) => {
const profileFollows = new Map(
(await db.events.where('kind').equals(3).toArray()).map((it) => [
it.pubkey,
it.tags.filter((it) => it.startsWith('p»')).map((it) => it.split('»', 3)[1])
])
);

// n-th degree follows' pubkeys
const follows: Array<Set<string>> = [];
// 0-th
follows[0] = new Set(pubkeys);

// n-th
let all = new Set<string>();
for (let i = 0; i < 10; i++) {
all = new Set(follows.flatMap((it) => [...it]));
if (all.size > 10000) {
console.log(`Exploring ${all.size} follows to the ${i}-th degree ...`);
break;
}
// 1st
const fArray = Array.from(follows[i])
.flatMap((it) => profileFollows.get(it)!)
.filter((it) => !all.has(it));
const newSet = new Set(fArray);
if (newSet.size == 0) {
console.log(
`Exploring ${all.size} follows to the ${i}-th degree. No more follows found in the ${
i + 1
}-th degree ...`
);
break;
}
follows[i + 1] = newSet;
}

profiles = profiles.map((profile) => {
const deg = follows.findIndex((it) => it.has(profile.pubkey));
profile.degree = deg > -1 ? deg : 100;
return profile;
});

return profiles;
};
35 changes: 35 additions & 0 deletions src/routes/+page.svelte
Original file line number Diff line number Diff line change
@@ -1,5 +1,33 @@
<script lang="ts">
import { db, type IEvent } from '../db';
import { cProfiles } from '../stores';
import TextNoteForm from '../components/TextNoteForm.svelte';
import TextNote from '../components/TextNote.svelte';
import { liveQuery, type Observable } from 'dexie';
import { filterMap } from '../utils/array';
let posts: Observable<IEvent[]>;
// just for testing, needs work
const update = (c) => {
if (c.backing.size === 0) return;
posts = liveQuery(async () => {
const profiles = filterMap(
[...$cProfiles.backing.values()],
(p) => p.degree < 3,
(p) => p.pubkey
);
const res = (
await db.events
.where('pubkey')
.anyOf(profiles)
.and((e) => e.kind === 1)
.reverse()
.sortBy('created_at')
).slice(0, 100);
return res;
});
};
cProfiles.subscribe((c) => update(c));
</script>

<svelte:head>
Expand All @@ -12,6 +40,13 @@
<div class="create-note-container">
<TextNoteForm />
</div>
<div class="posts">
{#if $posts instanceof Array}
{#each $posts as post}
<TextNote event={post} />
{/each}
{/if}
</div>

<style>
.create-note-container {
Expand Down
16 changes: 11 additions & 5 deletions src/stores.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { db } from './db';
import type { IProfile } from './db';
import { liveQuery, type Observable } from 'dexie';
import { derived, writable, type Readable } from 'svelte/store';
import { get, writable } from 'svelte/store';
import { getDegreesForPubkeys } from './nostrHelper';

export const activeProfile: Observable<IProfile | undefined> = liveQuery(
async (): Promise<IProfile | undefined> => {
Expand Down Expand Up @@ -39,6 +40,14 @@ export class ProfileCache {
public set(s: string, p: IProfile) {
return this.backing.set(s, p);
}

public async updateDegrees(profiles?: IProfile[]) {
if (profiles === undefined) profiles = await db.profiles.toArray();
const pubkey = (await db.config.get('activePubkey'))?.value;
const profilesWithDegrees = await getDegreesForPubkeys([pubkey], profiles);
this.backing = new Map(profilesWithDegrees.map((x) => [x.pubkey, x]));
cProfiles.set(this);
}
}

/**
Expand All @@ -50,8 +59,5 @@ export const cProfiles = writable(new ProfileCache());
const profiles = liveQuery(() => db.profiles.toArray());
profiles.subscribe((p) => {
console.log(`updating profile cache (->${p.length} profiles)`);
cProfiles.update((old) => {
old.backing = new Map(p.map((x) => [x.pubkey, x]));
return old;
});
get(cProfiles).updateDegrees(p);
});

0 comments on commit 1b3ae0f

Please sign in to comment.