mirror of
https://github.com/coracle-social/coracle.git
synced 2024-09-29 16:31:04 +00:00
Use social petnames
This commit is contained in:
parent
09fa5dd669
commit
82aab16605
@ -5,9 +5,7 @@ import {partition, uniqBy, sortBy, prop, always, pluck, without, is} from "ramda
|
||||
import {throttle} from "throttle-debounce"
|
||||
import {writable} from "svelte/store"
|
||||
import {ensurePlural, noop, createMap} from "hurdak/lib/hurdak"
|
||||
import {Tags} from "src/util/nostr"
|
||||
import {fuzzy} from "src/util/misc"
|
||||
import user from "src/agent/user"
|
||||
|
||||
const Adapter = window.indexedDB ? IncrementalIndexedDBAdapter : Loki.LokiMemoryAdapter
|
||||
|
||||
@ -164,11 +162,12 @@ type WatchStore<T> = Writable<T> & {
|
||||
refresh: () => void
|
||||
}
|
||||
|
||||
export const watch = (names, f) => {
|
||||
names = ensurePlural(names)
|
||||
export const watch = (namesOrTables, f) => {
|
||||
namesOrTables = ensurePlural(namesOrTables)
|
||||
|
||||
const store = writable(null) as WatchStore<any>
|
||||
const tables = names.map(name => (is(Table, name) ? name : registry[name]))
|
||||
const tables = namesOrTables.map(name => (is(Table, name) ? name : registry[name]))
|
||||
const names = pluck("name", tables)
|
||||
|
||||
// Initialize synchronously if possible
|
||||
const initialValue = f(...tables)
|
||||
@ -199,17 +198,7 @@ export const dropAll = () => new Promise(resolve => loki.deleteDatabase(resolve)
|
||||
const sortByCreatedAt = sortBy(e => -e.created_at)
|
||||
const sortByScore = sortBy(e => -e.score)
|
||||
|
||||
export const people = new Table("people", "pubkey", {
|
||||
max: 3000,
|
||||
// Don't delete the user's own profile or those of direct follows
|
||||
sort: xs => {
|
||||
const follows = Tags.wrap(user.getPetnames()).values().all()
|
||||
const whitelist = new Set(follows.concat(user.getPubkey()))
|
||||
|
||||
return sortBy(x => (whitelist.has(x.pubkey) ? 0 : x.created_at), xs)
|
||||
},
|
||||
})
|
||||
|
||||
export const people = new Table("people", "pubkey", {max: 3000})
|
||||
export const userEvents = new Table("userEvents", "id", {max: 2000, sort: sortByCreatedAt})
|
||||
export const notifications = new Table("notifications", "id", {sort: sortByCreatedAt})
|
||||
export const contacts = new Table("contacts", "pubkey")
|
||||
|
@ -138,19 +138,6 @@ addHandler(0, e => {
|
||||
})
|
||||
})
|
||||
|
||||
addHandler(3, e => {
|
||||
const person = people.get(e.pubkey)
|
||||
|
||||
if (e.created_at < person?.petnames_updated_at) {
|
||||
return
|
||||
}
|
||||
|
||||
updatePerson(e.pubkey, {
|
||||
petnames_updated_at: e.created_at,
|
||||
petnames: e.tags.filter(t => t[0] === "p"),
|
||||
})
|
||||
})
|
||||
|
||||
// User profile, except for events also handled for other users
|
||||
|
||||
const userHandler = cb => e => {
|
||||
|
@ -1,19 +1,6 @@
|
||||
import type {Relay, MyEvent} from "src/util/types"
|
||||
import type {Readable} from "svelte/store"
|
||||
import {
|
||||
slice,
|
||||
uniqBy,
|
||||
without,
|
||||
reject,
|
||||
prop,
|
||||
pipe,
|
||||
assoc,
|
||||
whereEq,
|
||||
when,
|
||||
concat,
|
||||
nth,
|
||||
map,
|
||||
} from "ramda"
|
||||
import {slice, uniqBy, without, reject, prop, pipe, assoc, whereEq, when, concat, map} from "ramda"
|
||||
import {findReplyId, findRootId} from "src/util/nostr"
|
||||
import {synced} from "src/util/misc"
|
||||
import {derived, get} from "svelte/store"
|
||||
@ -36,7 +23,6 @@ const profile = synced("agent/user/profile", {
|
||||
},
|
||||
rooms_joined: [],
|
||||
last_checked: {},
|
||||
petnames: [],
|
||||
relays: pool.defaultRelays,
|
||||
mutes: [],
|
||||
lists: [],
|
||||
@ -45,7 +31,6 @@ const profile = synced("agent/user/profile", {
|
||||
const settings = derived(profile, prop("settings"))
|
||||
const roomsJoined = derived(profile, prop("rooms_joined")) as Readable<string>
|
||||
const lastChecked = derived(profile, prop("last_checked")) as Readable<Record<string, number>>
|
||||
const petnames = derived(profile, prop("petnames")) as Readable<Array<Array<string>>>
|
||||
const relays = derived(profile, p =>
|
||||
pool.forceRelays.length > 0 ? pool.forceRelays : p.relays
|
||||
) as Readable<Array<Relay>>
|
||||
@ -110,13 +95,6 @@ export default {
|
||||
this.setAppData("rooms_joined/v1", without([id], profileCopy.rooms_joined))
|
||||
},
|
||||
|
||||
// Petnames
|
||||
|
||||
petnames,
|
||||
getPetnames: () => profileCopy.petnames,
|
||||
petnamePubkeys: derived(petnames, map(nth(1))) as Readable<Array<string>>,
|
||||
getPetnamePubkeys: () => profileCopy.petnames.map(nth(1)),
|
||||
|
||||
// Relays
|
||||
|
||||
relays,
|
||||
|
@ -9,19 +9,20 @@
|
||||
import {keys, social} from "src/system"
|
||||
import {sampleRelays, getPubkeyWriteRelays} from "src/agent/relays"
|
||||
import user from "src/agent/user"
|
||||
import {watch} from "src/agent/db"
|
||||
import pool from "src/agent/pool"
|
||||
import {addToList} from "src/app/state"
|
||||
|
||||
export let person
|
||||
|
||||
const npub = nip19.npubEncode(person.pubkey)
|
||||
const {mutes} = user
|
||||
const {canSign} = keys
|
||||
const {petnamePubkeys, mutes} = user
|
||||
const following = watch(social.graph, () => social.isUserFollowing(person.pubkey))
|
||||
|
||||
let actions = []
|
||||
|
||||
$: muted = find(m => m[1] === person.pubkey, $mutes)
|
||||
$: following = $petnamePubkeys.includes(person.pubkey)
|
||||
$: {
|
||||
actions = []
|
||||
|
||||
@ -91,13 +92,13 @@
|
||||
{#if $canSign}
|
||||
<Popover triggerType="mouseenter">
|
||||
<div slot="trigger">
|
||||
{#if following}
|
||||
{#if $following}
|
||||
<i class="fa fa-user-minus cursor-pointer" on:click={unfollow} />
|
||||
{:else if user.getPubkey() !== person.pubkey}
|
||||
<i class="fa fa-user-plus cursor-pointer" on:click={follow} />
|
||||
{/if}
|
||||
</div>
|
||||
<div slot="tooltip">{following ? "Unfollow" : "Follow"}</div>
|
||||
<div slot="tooltip">{$following ? "Unfollow" : "Follow"}</div>
|
||||
</Popover>
|
||||
{/if}
|
||||
<OverflowMenu {actions} />
|
||||
|
@ -6,26 +6,31 @@
|
||||
import Anchor from "src/partials/Anchor.svelte"
|
||||
import PersonCircle from "src/app/shared/PersonCircle.svelte"
|
||||
import PersonAbout from "src/app/shared/PersonAbout.svelte"
|
||||
import {keys, social} from "src/system"
|
||||
import {watch} from "src/agent/db"
|
||||
import {social} from "src/system"
|
||||
import {getPubkeyWriteRelays, sampleRelays} from "src/agent/relays"
|
||||
|
||||
const {canSign} = keys
|
||||
|
||||
export let person
|
||||
export let hasPetname = null
|
||||
|
||||
const unfollow = ({pubkey}) => social.unfollow(pubkey)
|
||||
const unfollow = async ({pubkey}) => {
|
||||
await social.unfollow(pubkey)
|
||||
|
||||
const follow = ({pubkey}) => {
|
||||
const [{url}] = sampleRelays(getPubkeyWriteRelays(pubkey))
|
||||
|
||||
social.follow(pubkey, url, displayPerson(person))
|
||||
isFollowing = getIsFollowing()
|
||||
}
|
||||
|
||||
const isFollowing = watch(social.graph, () =>
|
||||
const follow = async ({pubkey}) => {
|
||||
const [{url}] = sampleRelays(getPubkeyWriteRelays(pubkey))
|
||||
|
||||
await social.follow(pubkey, url, displayPerson(person))
|
||||
|
||||
isFollowing = getIsFollowing()
|
||||
}
|
||||
|
||||
const getIsFollowing = () =>
|
||||
hasPetname ? hasPetname(person.pubkey) : social.isUserFollowing(person.pubkey)
|
||||
)
|
||||
|
||||
// Set this manually to avoid a million listeners
|
||||
let isFollowing = getIsFollowing()
|
||||
</script>
|
||||
|
||||
<div in:fly={{y: 20}}>
|
||||
@ -46,14 +51,12 @@
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{#if $canSign}
|
||||
{#if isFollowing}
|
||||
<Anchor theme="button-accent" stopPropagation on:click={() => unfollow(person)}>
|
||||
Following
|
||||
</Anchor>
|
||||
{:else}
|
||||
<Anchor theme="button" stopPropagation on:click={() => follow(person)}>Follow</Anchor>
|
||||
{/if}
|
||||
{#if isFollowing}
|
||||
<Anchor theme="button-accent" stopPropagation on:click={() => unfollow(person)}>
|
||||
Following
|
||||
</Anchor>
|
||||
{:else}
|
||||
<Anchor theme="button" stopPropagation on:click={() => follow(person)}>Follow</Anchor>
|
||||
{/if}
|
||||
</div>
|
||||
<p class="overflow-hidden text-ellipsis">
|
||||
|
@ -1,10 +1,10 @@
|
||||
<script type="ts">
|
||||
import {onMount} from "svelte"
|
||||
import {uniq, sortBy, pluck} from "ramda"
|
||||
import {Tags} from "src/util/nostr"
|
||||
import Content from "src/partials/Content.svelte"
|
||||
import Spinner from "src/partials/Spinner.svelte"
|
||||
import PersonInfo from "src/app/shared/PersonInfo.svelte"
|
||||
import {social} from "src/system"
|
||||
import {sampleRelays, getPubkeyWriteRelays} from "src/agent/relays"
|
||||
import {getPersonWithFallback} from "src/agent/db"
|
||||
import {watch} from "src/agent/db"
|
||||
@ -15,14 +15,13 @@
|
||||
|
||||
let pubkeys = []
|
||||
|
||||
const person = getPersonWithFallback(pubkey)
|
||||
const people = watch("people", t => {
|
||||
return sortBy(p => (p.kind0 ? 0 : 1), pubkeys.map(getPersonWithFallback))
|
||||
})
|
||||
|
||||
onMount(async () => {
|
||||
if (type === "follows") {
|
||||
pubkeys = Tags.wrap(person.petnames).values().all()
|
||||
pubkeys = social.getFollows(pubkey)
|
||||
people.refresh()
|
||||
} else {
|
||||
await network.load({
|
||||
|
@ -1,51 +0,0 @@
|
||||
<script>
|
||||
import {nth} from "ramda"
|
||||
import {debounce} from "throttle-debounce"
|
||||
import Input from "src/partials/Input.svelte"
|
||||
import Spinner from "src/partials/Spinner.svelte"
|
||||
import PersonInfo from "src/app/shared/PersonInfo.svelte"
|
||||
import {sampleRelays} from "src/agent/relays"
|
||||
import {searchPeople} from "src/agent/db"
|
||||
import network from "src/agent/network"
|
||||
import user from "src/agent/user"
|
||||
|
||||
export let hideFollows = false
|
||||
|
||||
let q
|
||||
|
||||
const {petnames} = user
|
||||
|
||||
const loadPeople = debounce(500, search => {
|
||||
if (q.length > 2) {
|
||||
network.load({
|
||||
relays: sampleRelays([{url: "wss://relay.nostr.band"}]),
|
||||
filter: [{kinds: [0], search, limit: 10}],
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
$: loadPeople(q)
|
||||
|
||||
$: results = $searchPeople(q)
|
||||
.filter(person => {
|
||||
if (person.pubkey === user.getPubkey()) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (hideFollows && $petnames.map(nth(1)).includes(person.pubkey)) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
})
|
||||
.slice(0, 50)
|
||||
</script>
|
||||
|
||||
<Input bind:value={q} placeholder="Search for people">
|
||||
<i slot="before" class="fa-solid fa-search" />
|
||||
</Input>
|
||||
{#each results as person (person.pubkey)}
|
||||
<PersonInfo {person} />
|
||||
{:else}
|
||||
<Spinner />
|
||||
{/each}
|
@ -4,12 +4,15 @@
|
||||
import {tweened} from "svelte/motion"
|
||||
import {numberFmt} from "src/util/misc"
|
||||
import {modal} from "src/partials/state"
|
||||
import {social} from "src/system"
|
||||
import {watch} from "src/agent/db"
|
||||
import {sampleRelays, getPubkeyWriteRelays} from "src/agent/relays"
|
||||
import network from "src/agent/network"
|
||||
import pool from "src/agent/pool"
|
||||
|
||||
export let person
|
||||
|
||||
const followsCount = watch(social.graph, () => social.getFollowsSet(person.pubkey).size)
|
||||
const interpolate = (a, b) => t => a + Math.round((b - a) * t)
|
||||
|
||||
let followersCount = tweened(0, {interpolate, duration: 1000})
|
||||
@ -47,13 +50,11 @@
|
||||
})
|
||||
</script>
|
||||
|
||||
{#if person?.petnames}
|
||||
<div class="flex gap-8" in:fly={{y: 20}}>
|
||||
<button on:click={showFollows}>
|
||||
<strong>{person.petnames.length}</strong> following
|
||||
</button>
|
||||
<button on:click={showFollowers}>
|
||||
<strong>{numberFmt.format($followersCount)}</strong> followers
|
||||
</button>
|
||||
</div>
|
||||
{/if}
|
||||
<div class="flex gap-8" in:fly={{y: 20}}>
|
||||
<button on:click={showFollows}>
|
||||
<strong>{$followsCount}</strong> following
|
||||
</button>
|
||||
<button on:click={showFollowers}>
|
||||
<strong>{numberFmt.format($followersCount)}</strong> followers
|
||||
</button>
|
||||
</div>
|
||||
|
@ -14,11 +14,11 @@
|
||||
export let pubkey
|
||||
|
||||
const {canSign} = keys
|
||||
const {petnamePubkeys, mutes} = user
|
||||
const following = watch(social.graph, () => social.isUserFollowing(pubkey))
|
||||
const {mutes} = user
|
||||
const getRelays = () => sampleRelays(getPubkeyWriteRelays(pubkey))
|
||||
const person = watch("people", () => getPersonWithFallback(pubkey))
|
||||
|
||||
$: following = $petnamePubkeys.includes(pubkey)
|
||||
$: muted = $mutes.map(nth(1)).includes(pubkey)
|
||||
|
||||
const follow = () => {
|
||||
@ -58,7 +58,7 @@
|
||||
{:else}
|
||||
<i title="Mute" class="fa fa-microphone w-6 cursor-pointer text-center" on:click={mute} />
|
||||
{/if}
|
||||
{#if following}
|
||||
{#if $following}
|
||||
<i
|
||||
title="Unfollow"
|
||||
class="fa fa-user-minus w-6 cursor-pointer text-center"
|
||||
|
@ -3,7 +3,6 @@
|
||||
import {generatePrivateKey} from "nostr-tools"
|
||||
import {fly} from "src/util/transition"
|
||||
import {navigate} from "svelte-routing"
|
||||
import {shuffle} from "src/util/misc"
|
||||
import {displayPerson} from "src/util/nostr"
|
||||
import OnboardingIntro from "src/app/views/OnboardingIntro.svelte"
|
||||
import OnboardingProfile from "src/app/views/OnboardingProfile.svelte"
|
||||
@ -46,7 +45,7 @@
|
||||
cmd.updateUser(profile).publish(user.getRelays()),
|
||||
note && cmd.createNote(note).publish(user.getRelays()),
|
||||
social.updatePetnames(
|
||||
user.getPetnamePubkeys().map(pubkey => {
|
||||
social.getUserFollows().map(pubkey => {
|
||||
const [{url}] = sampleRelays(getPubkeyWriteRelays(pubkey))
|
||||
const name = displayPerson(getPersonWithFallback(pubkey))
|
||||
|
||||
@ -61,16 +60,11 @@
|
||||
navigate("/notes")
|
||||
}
|
||||
|
||||
// Prime our people cache for hardcoded follows and a sample of people they follow
|
||||
onMount(async () => {
|
||||
const relays = sampleRelays(user.getRelays())
|
||||
const follows = user.getPetnamePubkeys().concat(DEFAULT_FOLLOWS)
|
||||
|
||||
await network.loadPeople(follows, {relays})
|
||||
|
||||
const others = shuffle(social.getNetwork(follows)).slice(0, 256)
|
||||
|
||||
await network.loadPeople(others, {relays})
|
||||
onMount(() => {
|
||||
// Prime our database with some defaults
|
||||
network.loadPeople(DEFAULT_FOLLOWS, {
|
||||
relays: sampleRelays(user.getRelays()),
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
|
@ -7,14 +7,13 @@
|
||||
import Content from "src/partials/Content.svelte"
|
||||
import PersonInfo from "src/app/shared/PersonInfo.svelte"
|
||||
import {DEFAULT_FOLLOWS, social} from "src/system"
|
||||
import {getPersonWithFallback, searchPeople} from "src/agent/db"
|
||||
import {watch, getPersonWithFallback, searchPeople} from "src/agent/db"
|
||||
import {sampleRelays, getPubkeyWriteRelays} from "src/agent/relays"
|
||||
import {modal} from "src/partials/state"
|
||||
import user from "src/agent/user"
|
||||
|
||||
const {petnamePubkeys} = user
|
||||
const follows = watch(social.graph, social.getUserFollowsSet)
|
||||
|
||||
if ($petnamePubkeys.length === 0) {
|
||||
if ($follows.size === 0) {
|
||||
social.updatePetnames(
|
||||
DEFAULT_FOLLOWS.map(pubkey => {
|
||||
const [{url}] = sampleRelays(getPubkeyWriteRelays(pubkey))
|
||||
@ -27,7 +26,7 @@
|
||||
|
||||
let q = ""
|
||||
|
||||
$: results = reject(p => $petnamePubkeys.includes(p.pubkey), $searchPeople(q))
|
||||
$: results = reject(p => $follows.has(p.pubkey), $searchPeople(q))
|
||||
</script>
|
||||
|
||||
<Content>
|
||||
@ -47,13 +46,13 @@
|
||||
<i class="fa fa-user-astronaut fa-lg" />
|
||||
<h2 class="staatliches text-2xl">Your follows</h2>
|
||||
</div>
|
||||
{#if $petnamePubkeys.length === 0}
|
||||
{#if $follows.size === 0}
|
||||
<div class="mt-8 flex items-center justify-center gap-2 text-center">
|
||||
<i class="fa fa-triangle-exclamation" />
|
||||
<span>No follows selected</span>
|
||||
</div>
|
||||
{:else}
|
||||
{#each $petnamePubkeys as pubkey}
|
||||
{#each Array.from($follows) as pubkey}
|
||||
<PersonInfo person={getPersonWithFallback(pubkey)} />
|
||||
{/each}
|
||||
{/if}
|
||||
|
@ -5,16 +5,14 @@
|
||||
import PersonBadge from "src/app/shared/PersonBadge.svelte"
|
||||
import ContentEditable from "src/partials/ContentEditable.svelte"
|
||||
import Suggestions from "src/partials/Suggestions.svelte"
|
||||
import {social} from "src/system"
|
||||
import {searchPeople} from "src/agent/db"
|
||||
import user from "src/agent/user"
|
||||
import {getPubkeyWriteRelays} from "src/agent/relays"
|
||||
|
||||
export let onSubmit
|
||||
|
||||
let contenteditable, suggestions
|
||||
|
||||
const {petnamePubkeys} = user
|
||||
|
||||
const pubkeyEncoder = {
|
||||
encode: pubkey => {
|
||||
const relays = pluck("url", getPubkeyWriteRelays(pubkey))
|
||||
@ -32,7 +30,7 @@
|
||||
let results = []
|
||||
if (word.length > 1 && word.startsWith("@")) {
|
||||
const [followed, notFollowed] = partition(
|
||||
p => $petnamePubkeys.includes(p.pubkey),
|
||||
p => social.isUserFollowing(p.pubkey),
|
||||
$searchPeople(word.slice(1))
|
||||
)
|
||||
|
||||
|
@ -37,14 +37,12 @@ export default ({keys, sync, cmd, getUserWriteRelays}) => {
|
||||
|
||||
const getPetnames = pubkey => graph.get(pubkey)?.petnames || []
|
||||
|
||||
const getPetnamePubkeys = pubkey => getPetnames(pubkey).map(t => t[1])
|
||||
|
||||
const getFollowsSet = pubkeys => {
|
||||
const follows = new Set()
|
||||
|
||||
for (const pubkey of ensurePlural(pubkeys)) {
|
||||
for (const follow of getPetnamePubkeys(pubkey)) {
|
||||
follows.add(follow)
|
||||
for (const tag of getPetnames(pubkey)) {
|
||||
follows.add(tag[1])
|
||||
}
|
||||
}
|
||||
|
||||
@ -72,7 +70,9 @@ export default ({keys, sync, cmd, getUserWriteRelays}) => {
|
||||
|
||||
const getUserKey = () => keys.getPubkey() || "anonymous"
|
||||
const getUserPetnames = () => getPetnames(getUserKey())
|
||||
const getUserFollowsSet = () => getFollowsSet(getUserKey())
|
||||
const getUserFollows = () => getFollows(getUserKey())
|
||||
const getUserNetworkSet = () => getNetworkSet(getUserKey())
|
||||
const getUserNetwork = () => getNetwork(getUserKey())
|
||||
const isUserFollowing = pubkey => isFollowing(getUserKey(), pubkey)
|
||||
|
||||
@ -100,12 +100,15 @@ export default ({keys, sync, cmd, getUserWriteRelays}) => {
|
||||
return {
|
||||
graph,
|
||||
getPetnames,
|
||||
getPetnamePubkeys,
|
||||
getFollowsSet,
|
||||
getFollows,
|
||||
getNetworkSet,
|
||||
getNetwork,
|
||||
isFollowing,
|
||||
getUserPetnames,
|
||||
getUserFollowsSet,
|
||||
getUserFollows,
|
||||
getUserNetworkSet,
|
||||
getUserNetwork,
|
||||
isUserFollowing,
|
||||
updatePetnames,
|
||||
|
@ -9,7 +9,6 @@ export type Relay = {
|
||||
|
||||
export type Person = {
|
||||
pubkey: string
|
||||
petnames?: Array<Array<string>>
|
||||
relays?: Array<Relay>
|
||||
mutes?: Array<Array<string>>
|
||||
kind0?: {
|
||||
|
Loading…
Reference in New Issue
Block a user