Rename social to nip02

This commit is contained in:
Jonathan Staab 2023-07-13 13:50:35 -07:00
parent ad8a214752
commit a650412150
23 changed files with 168 additions and 138 deletions

View File

@ -85,7 +85,7 @@
seenChallenges.add(challenge)
const rawEvent = engine.Builder.authenticate(url, challenge)
const [event] = await engine.User.publish(rawEvent, [url], null, "AUTH")
const [event] = await engine.Outbox.publish(rawEvent, [url], null, "AUTH")
return event
}

View File

@ -46,12 +46,13 @@ export const events = engine.Events
export const keys = engine.Keys
export const meta = engine.Meta
export const network = engine.Network
export const nip02 = engine.Nip02
export const nip04 = engine.Nip04
export const nip05 = engine.Nip05
export const nip28 = engine.Nip28
export const nip57 = engine.Nip57
export const pubkeyLoader = engine.PubkeyLoader
export const nip65 = engine.Nip65
export const social = engine.Social
export const outbox = engine.Outbox
export const pubkeyLoader = engine.PubkeyLoader
export const storage = engine.Storage
export const user = engine.User

View File

@ -13,7 +13,7 @@
import NoteReply from "src/app/shared/NoteReply.svelte"
import NoteActions from "src/app/shared/NoteActions.svelte"
import Card from "src/partials/Card.svelte"
import {nip05, user, directory, nip65, social} from "src/app/engine"
import {nip05, user, directory, nip65, nip02} from "src/app/engine"
import NoteContent from "src/app/shared/NoteContent.svelte"
export let note
@ -36,7 +36,7 @@
const showEntire = anchorId === note.id
const interactive = !anchorId || !showEntire
const author = directory.profiles.key(note.pubkey).derived(defaultTo({pubkey: note.pubkey}))
const muted = social.graph.derived(() => user.isIgnoring(note.id))
const muted = nip02.graph.derived(() => user.isIgnoring(note.id))
const handle = nip05.handles.key(note.pubkey)
let border, childrenContainer, noteContainer

View File

@ -14,7 +14,16 @@
import CopyValue from "src/partials/CopyValue.svelte"
import PersonBadge from "src/app/shared/PersonBadge.svelte"
import RelayCard from "src/app/shared/RelayCard.svelte"
import {ENABLE_ZAPS, FORCE_RELAYS, nip57, builder, nip65, keys, user} from "src/app/engine"
import {
ENABLE_ZAPS,
FORCE_RELAYS,
nip57,
builder,
nip65,
keys,
outbox,
user,
} from "src/app/engine"
export let note
export let reply
@ -41,11 +50,11 @@
const react = async content => {
const relays = nip65.getPublishHints(3, note, user.getRelayUrls("write"))
like = first(await user.publish(builder.createReaction(note, content), relays))
like = first(await outbox.publish(builder.createReaction(note, content), relays))
}
const deleteReaction = e => {
user.publish(
outbox.publish(
builder.deleteEvents([e.id]),
nip65.getPublishHints(3, note, user.getRelayUrls("write"))
)

View File

@ -4,14 +4,15 @@
import {modal} from "src/partials/state"
import Popover from "src/partials/Popover.svelte"
import OverflowMenu from "src/partials/OverflowMenu.svelte"
import {FORCE_RELAYS, keys, user, social} from "src/app/engine"
import {FORCE_RELAYS, keys, user, nip02} from "src/app/engine"
import {addToList} from "src/app/state"
export let pubkey
const npub = nip19.npubEncode(pubkey)
const following = social.graph.key(keys.pubkey.get()).derived(() => user.isFollowing(pubkey))
const muted = social.graph.key(keys.pubkey.get()).derived(() => user.isIgnoring(pubkey))
const graphEntry = nip02.graph.key(keys.pubkey.get())
const following = graphEntry.derived(() => user.isFollowing(pubkey))
const muted = graphEntry.derived(() => user.isIgnoring(pubkey))
let actions = []

View File

@ -5,7 +5,7 @@
import Content from "src/partials/Content.svelte"
import Spinner from "src/partials/Spinner.svelte"
import PersonInfo from "src/app/shared/PersonInfo.svelte"
import {social, nip65, user, network, pubkeyLoader} from "src/app/engine"
import {nip02, nip65, user, network, pubkeyLoader} from "src/app/engine"
export let type
export let pubkey
@ -14,7 +14,7 @@
onMount(async () => {
if (type === "follows") {
pubkeys = social.getFollows(pubkey)
pubkeys = nip02.getFollows(pubkey)
} else {
await network.load({
shouldProcess: false,

View File

@ -4,11 +4,11 @@
import {tweened} from "svelte/motion"
import {numberFmt, batch} from "src/util/misc"
import {modal} from "src/partials/state"
import {social, user, nip65, network} from "src/app/engine"
import {nip02, user, nip65, network} from "src/app/engine"
export let pubkey
const followsCount = social.graph.key(pubkey).derived(() => social.getFollowsSet(pubkey).size)
const followsCount = nip02.graph.key(pubkey).derived(() => nip02.getFollowsSet(pubkey).size)
const interpolate = (a, b) => t => a + Math.round((b - a) * t)
let followersCount = tweened(0, {interpolate, duration: 1000})

View File

@ -1,7 +1,7 @@
<script lang="ts">
import {defaultTo} from "ramda"
import Anchor from "src/partials/Anchor.svelte"
import {directory, social, nip05} from "src/app/engine"
import {directory, nip02, nip05} from "src/app/engine"
import PersonCircle from "src/app/shared/PersonCircle.svelte"
import PersonAbout from "src/app/shared/PersonAbout.svelte"
import {keys, user} from "src/app/engine"
@ -9,8 +9,9 @@
export let pubkey
const following = social.graph.key(keys.pubkey.get()).derived(() => user.isFollowing(pubkey))
const muted = social.graph.key(keys.pubkey.get()).derived(() => user.isIgnoring(pubkey))
const graphEntry = nip02.graph.key(keys.pubkey.get())
const following = graphEntry.derived(() => user.isFollowing(pubkey))
const muted = graphEntry.derived(() => user.isIgnoring(pubkey))
const profile = directory.profiles.key(pubkey).derived(defaultTo({pubkey}))
const handle = nip05.handles.key(pubkey)
const unfollow = () => user.unfollow(pubkey)

View File

@ -8,7 +8,7 @@
import Toggle from "src/partials/Toggle.svelte"
import Rating from "src/partials/Rating.svelte"
import Anchor from "src/partials/Anchor.svelte"
import {user, nip65, meta, keys} from "src/app/engine"
import {user, directory, nip65, meta, keys} from "src/app/engine"
import {loadAppData} from "src/app/state"
export let relay
@ -22,17 +22,17 @@
let quality = null
let message = null
const relays = nip65.policies.key(keys.pubkey.get()).derived(() => new Set(user.getRelayUrls()))
const {pubkey} = keys
const relays = nip65.policies.key($pubkey).derived(() => new Set(user.getRelayUrls()))
const removeRelay = r => user.removeRelay(r.url)
const addRelay = r => {
user.addRelay(r.url)
const pubkey = user.getPubkey()
if (pubkey && !user.getProfile().created_at) {
loadAppData(pubkey)
if ($pubkey && !directory.profiles.hasKey($pubkey)) {
loadAppData($pubkey)
}
}

View File

@ -21,6 +21,7 @@ import {
nip28,
meta,
network,
outbox,
user,
keys,
} from "src/app/engine"
@ -205,7 +206,7 @@ export const mergeParents = (notes: Array<DisplayEvent>) => {
}
export const publishWithToast = (event, relays) =>
user.publish(event, relays, ({completed, succeeded, failed, timeouts, pending}) => {
outbox.publish(event, relays, ({completed, succeeded, failed, timeouts, pending}) => {
let message = `Published to ${succeeded.size}/${relays.length} relays`
const extra = []

View File

@ -8,7 +8,7 @@
import Anchor from "src/partials/Anchor.svelte"
import PersonBadge from "src/app/shared/PersonBadge.svelte"
import NoteContent from "src/app/shared/NoteContent.svelte"
import {builder, nip28, user, nip65, network} from "src/app/engine"
import {builder, nip28, user, outbox, nip65, network} from "src/app/engine"
export let entity
@ -24,9 +24,9 @@
}
const sendMessage = async content => {
const [hint] = getRelays()
const relays = getRelays()
await user.publish(builder.createChatMessage(id, content, hint), getRelays())
await outbox.publish(builder.createChatMessage(id, content, relays[0]), relays)
}
onMount(() => {

View File

@ -7,7 +7,7 @@
import Channel from "src/partials/Channel.svelte"
import Anchor from "src/partials/Anchor.svelte"
import NoteContent from "src/app/shared/NoteContent.svelte"
import {user, nip04, nip65, directory, builder, network} from "src/app/engine"
import {user, nip04, nip65, outbox, directory, builder, network} from "src/app/engine"
import {routes} from "src/app/state"
import PersonCircle from "src/app/shared/PersonCircle.svelte"
import PersonAbout from "src/app/shared/PersonAbout.svelte"
@ -28,7 +28,10 @@
const sendMessage = async content => {
const cyphertext = await user.crypt.encrypt(pubkey, content)
const [event] = await user.publish(builder.createDirectMessage(pubkey, cyphertext), getRelays())
const [event] = await outbox.publish(
builder.createDirectMessage(pubkey, cyphertext),
getRelays()
)
// Return unencrypted content so we can display it immediately
return {...event, content}

View File

@ -9,7 +9,15 @@
import OnboardingRelays from "src/app/views/OnboardingRelays.svelte"
import OnboardingFollows from "src/app/views/OnboardingFollows.svelte"
import OnboardingNote from "src/app/views/OnboardingNote.svelte"
import {DEFAULT_FOLLOWS, DEFAULT_RELAYS, pubkeyLoader, builder, user, keys} from "src/app/engine"
import {
DEFAULT_FOLLOWS,
DEFAULT_RELAYS,
pubkeyLoader,
outbox,
builder,
user,
keys,
} from "src/app/engine"
import {loadAppData} from "src/app/state"
import {modal} from "src/partials/state"
@ -33,7 +41,7 @@
user.setRelays(relays),
user.setProfile(profile),
user.setPetnames(petnames),
note && user.publish(builder.createNote(note)),
note && outbox.publish(builder.createNote(note)),
])
loadAppData(keys.pubkey.get())

View File

@ -5,11 +5,11 @@
import Heading from "src/partials/Heading.svelte"
import Content from "src/partials/Content.svelte"
import PersonInfo from "src/app/shared/PersonInfo.svelte"
import {DEFAULT_FOLLOWS, social, user, directory, builder} from "src/app/engine"
import {DEFAULT_FOLLOWS, nip02, user, directory, builder} from "src/app/engine"
import {modal} from "src/partials/state"
const {searchProfiles} = directory
const follows = social.graph.derived(() => user.getFollowsSet())
const follows = nip02.graph.derived(() => user.getFollowsSet())
if ($follows.size === 0) {
user.setPetnames(DEFAULT_FOLLOWS.map(builder.mention))

View File

@ -6,7 +6,7 @@
import Heading from "src/partials/Heading.svelte"
import Compose from "src/partials/Compose.svelte"
import Rating from "src/partials/Rating.svelte"
import {builder, user} from "src/app/engine"
import {builder, outbox} from "src/app/engine"
export let url
@ -25,7 +25,7 @@
],
})
user.publish(event)
outbox.publish(event)
modal.pop()
}

View File

@ -8,12 +8,10 @@
import Anchor from "src/partials/Anchor.svelte"
import Content from "src/partials/Content.svelte"
import Heading from "src/partials/Heading.svelte"
import {keys, user, builder} from "src/app/engine"
import {directory, keys, user, builder} from "src/app/engine"
import {routes} from "src/app/state"
import {publishWithToast} from "src/app/state"
let values = user.getProfile()
const nip05Url = "https://github.com/nostr-protocol/nips/blob/master/05.md"
const lud16Url = "https://blog.getalby.com/create-your-lightning-address/"
const pseudUrl =
@ -30,9 +28,13 @@
event?.preventDefault()
publishWithToast(builder.setProfile(values), relays)
navigate(routes.person(user.getPubkey(), "notes"))
navigate(routes.person($pubkey, "notes"))
}
const {pubkey} = keys
let values = directory.getProfile($pubkey)
document.title = "Profile"
</script>

View File

@ -21,9 +21,11 @@ export class Alerts {
static initialize({Alerts, Events, Keys, User}) {
const isMention = e => Tags.from(e).pubkeys().includes(Keys.pubkey.get())
const isDescendant = e => User.isUserEvent(findRootId(e))
const isUserEvent = id => Events.cache.getKey(id)?.pubkey === Keys.pubkey.get()
const isReply = e => User.isUserEvent(findReplyId(e))
const isDescendant = e => isUserEvent(findRootId(e))
const isReply = e => isUserEvent(findReplyId(e))
const handleNotification = condition => e => {
const pubkey = Keys.pubkey.get()

View File

@ -4,17 +4,17 @@ import {Tags} from "src/util/nostr"
import type {GraphEntry} from "src/engine/types"
import {collection} from "../util/store"
export class Social {
export class Nip02 {
static contributeState() {
const graph = collection<GraphEntry>()
return {graph}
}
static contributeActions({Social}) {
const getPetnames = pubkey => Social.graph.getKey(pubkey)?.petnames || []
static contributeActions({Nip02}) {
const getPetnames = pubkey => Nip02.graph.getKey(pubkey)?.petnames || []
const getMutedTags = pubkey => Social.graph.getKey(pubkey)?.mutes || []
const getMutedTags = pubkey => Nip02.graph.getKey(pubkey)?.mutes || []
const getFollowsSet = pubkeys => {
const follows = new Set()
@ -77,15 +77,15 @@ export class Social {
}
}
static initialize({Events, Social}) {
static initialize({Events, Nip02}) {
Events.addHandler(3, e => {
const entry = Social.graph.getKey(e.pubkey)
const entry = Nip02.graph.getKey(e.pubkey)
if (e.created_at < entry?.petnames_updated_at) {
return
}
Social.graph.mergeKey(e.pubkey, {
Nip02.graph.mergeKey(e.pubkey, {
pubkey: e.pubkey,
updated_at: now(),
petnames_updated_at: e.created_at,
@ -94,13 +94,13 @@ export class Social {
})
Events.addHandler(10000, e => {
const entry = Social.graph.getKey(e.pubkey)
const entry = Nip02.graph.getKey(e.pubkey)
if (e.created_at < entry?.mutes_updated_at) {
return
}
Social.graph.mergeKey(e.pubkey, {
Nip02.graph.mergeKey(e.pubkey, {
pubkey: e.pubkey,
updated_at: now(),
mutes_updated_at: e.created_at,

View File

@ -0,0 +1,48 @@
import {getEventHash} from "nostr-tools"
import {assoc} from "ramda"
import {doPipe} from "hurdak/lib/hurdak"
import {now} from "src/util/misc"
import {queue} from "../util/queue"
export class Outbox {
static contributeState() {
return {
queue: queue(),
}
}
static contributeActions({Keys, Network, User, Events}) {
const prepEvent = async rawEvent => {
return doPipe(rawEvent, [
assoc("created_at", now()),
assoc("pubkey", Keys.pubkey.get()),
e => ({...e, id: getEventHash(e)}),
Keys.sign,
])
}
const publish = async (event, relays = null, onProgress = null, verb = "EVENT") => {
if (!event.sig) {
event = await prepEvent(event)
}
if (!relays) {
relays = User.getRelayUrls("write")
}
// return console.log(event)
const promise = Network.publish({event, relays, onProgress, verb})
Events.queue.push(event)
return [event, promise]
}
return {prepEvent, publish}
}
static initialize({Outbox}) {
Outbox.queue.listen(Outbox.publish)
}
}

View File

@ -217,6 +217,9 @@ export class Storage {
"Events.cache": {
sortEntries: sortByPubkeyWhitelist(prop("created_at")),
},
"Nip02.graph": {
sortEntries: sortByPubkeyWhitelist(prop("updated_at")),
},
"Nip05.handles": {
sortEntries: sortByPubkeyWhitelist(prop("updated_at")),
},
@ -230,9 +233,6 @@ export class Storage {
"Nip65.policies": {
sortEntries: sortByPubkeyWhitelist(prop("updated_at")),
},
"Social.graph": {
sortEntries: sortByPubkeyWhitelist(prop("updated_at")),
},
})
}
}

View File

@ -1,18 +1,4 @@
import {getEventHash} from "nostr-tools"
import {
when,
prop,
uniq,
pluck,
without,
fromPairs,
whereEq,
find,
slice,
assoc,
reject,
} from "ramda"
import {doPipe} from "hurdak/lib/hurdak"
import {when, prop, uniq, pluck, without, fromPairs, whereEq, find, slice, reject} from "ramda"
import {now} from "src/util/misc"
import {Tags, appDataKeys, normalizeRelayUrl, findReplyId, findRootId} from "src/util/nostr"
import {writable} from "../util/store"
@ -33,56 +19,24 @@ export class User {
}
static contributeActions({
Keys,
Builder,
Content,
Crypt,
Directory,
Events,
Keys,
Network,
Crypt,
Builder,
Social,
User,
Outbox,
Nip02,
Nip04,
Nip28,
Nip65,
Content,
User,
}) {
const getPubkey = () => Keys.pubkey.get()
const getProfile = () => Directory.getProfile(getPubkey())
const isUserEvent = id => Events.cache.getKey(id)?.pubkey === getPubkey()
const getStateKey = () => (Keys.canSign.get() ? getPubkey() : "anonymous")
// Publish
const prepEvent = async rawEvent => {
return doPipe(rawEvent, [
assoc("created_at", now()),
assoc("pubkey", getPubkey()),
e => ({...e, id: getEventHash(e)}),
Keys.sign,
])
}
const publish = async (event, relays = null, onProgress = null, verb = "EVENT") => {
if (!event.sig) {
event = await prepEvent(event)
}
if (!relays) {
relays = getRelayUrls("write")
}
// return console.log(event)
const promise = Network.publish({event, relays, onProgress, verb})
Events.queue.push(event)
return [event, promise]
}
// Settings
const getSetting = k => User.settings.get()[k]
@ -96,16 +50,14 @@ export class User {
const d = appDataKeys.USER_SETTINGS
const v = await Crypt.encryptJson(settings)
return publish(Builder.setAppData(d, v))
return Outbox.queue.push({event: Builder.setAppData(d, v)})
}
}
const setAppData = async (d, content) => {
if (Keys.canSign.get()) {
const v = await Crypt.encryptJson(content)
const v = await Crypt.encryptJson(content)
return publish(Builder.setAppData(d, v))
}
return Outbox.queue.push({event: Builder.setAppData(d, v)})
}
// Nip65
@ -116,7 +68,7 @@ export class User {
const setRelays = relays => {
if (Keys.canSign.get()) {
return publish(Builder.setRelays(relays))
return Outbox.queue.push({event: Builder.setRelays(relays)})
} else {
Nip65.setPolicy({pubkey: getStateKey(), created_at: now()}, relays)
}
@ -130,35 +82,35 @@ export class User {
const setRelayPolicy = (url, policy) =>
setRelays(getRelays().map(when(whereEq({url}), p => ({...p, ...policy}))))
// Social
// Nip02
const getPetnames = () => Social.getPetnames(getStateKey())
const getPetnames = () => Nip02.getPetnames(getStateKey())
const getMutedTags = () => Social.getMutedTags(getStateKey())
const getMutedTags = () => Nip02.getMutedTags(getStateKey())
const getFollowsSet = () => Social.getFollowsSet(getStateKey())
const getFollowsSet = () => Nip02.getFollowsSet(getStateKey())
const getMutesSet = () => Social.getMutesSet(getStateKey())
const getMutesSet = () => Nip02.getMutesSet(getStateKey())
const getFollows = () => Social.getFollows(getStateKey())
const getFollows = () => Nip02.getFollows(getStateKey())
const getMutes = () => Social.getMutes(getStateKey())
const getMutes = () => Nip02.getMutes(getStateKey())
const getNetworkSet = () => Social.getNetworkSet(getStateKey())
const getNetworkSet = () => Nip02.getNetworkSet(getStateKey())
const getNetwork = () => Social.getNetwork(getStateKey())
const getNetwork = () => Nip02.getNetwork(getStateKey())
const isFollowing = pubkey => Social.isFollowing(getStateKey(), pubkey)
const isFollowing = pubkey => Nip02.isFollowing(getStateKey(), pubkey)
const isIgnoring = pubkeyOrEventId => Social.isIgnoring(getStateKey(), pubkeyOrEventId)
const isIgnoring = pubkeyOrEventId => Nip02.isIgnoring(getStateKey(), pubkeyOrEventId)
const setProfile = $profile => publish(Builder.setProfile($profile))
const setProfile = $profile => Outbox.queue.push({event: Builder.setProfile($profile)})
const setPetnames = async $petnames => {
if (Keys.canSign.get()) {
await publish(Builder.setPetnames($petnames))
await Outbox.queue.push({event: Builder.setPetnames($petnames)})
} else {
Social.graph.mergeKey(getStateKey(), {
Nip02.graph.mergeKey(getStateKey(), {
pubkey: getStateKey(),
updated_at: now(),
petnames_updated_at: now(),
@ -186,9 +138,9 @@ export class User {
const setMutes = async $mutes => {
if (Keys.canSign.get()) {
await publish(Builder.setMutes($mutes.map(slice(0, 2))))
await Outbox.queue.push({event: Builder.setMutes($mutes.map(slice(0, 2)))})
} else {
Social.graph.mergeKey(getStateKey(), {
Nip02.graph.mergeKey(getStateKey(), {
pubkey: getStateKey(),
updated_at: now(),
mutes_updated_at: now(),
@ -207,9 +159,11 @@ export class User {
const getLists = f => Content.getLists(l => l.pubkey === getStateKey() && (f ? f(l) : true))
const putList = (name, params, relays) =>
publish(Builder.createList([["d", name]].concat(params).concat(relays)))
Outbox.queue.push({
event: Builder.createList([["d", name]].concat(params).concat(relays)),
})
const removeList = naddr => publish(Builder.deleteNaddrs([naddr]))
const removeList = naddr => Outbox.queue.push({event: Builder.deleteNaddrs([naddr])})
// Messages
@ -264,11 +218,7 @@ export class User {
return {
getPubkey,
getProfile,
isUserEvent,
getStateKey,
prepEvent,
publish,
getSetting,
dufflepud,
setSettings,

View File

@ -7,13 +7,14 @@ import {Events} from "./components/Events"
import {Keys} from "./components/Keys"
import {Meta} from "./components/Meta"
import {Network} from "./components/Network"
import {Nip02} from "./components/Nip02"
import {Nip04} from "./components/Nip04"
import {Nip05} from "./components/Nip05"
import {Nip28} from "./components/Nip28"
import {Nip57} from "./components/Nip57"
import {Nip65} from "./components/Nip65"
import {Outbox} from "./components/Outbox"
import {PubkeyLoader} from "./components/PubkeyLoader"
import {Social} from "./components/Social"
import {Storage} from "./components/Storage"
import {User} from "./components/User"
@ -58,13 +59,14 @@ export const createDefaultEngine = Env => {
Keys,
Meta,
Network,
Nip02,
Nip04,
Nip05,
Nip28,
Nip57,
Nip65,
Outbox,
PubkeyLoader,
Social,
Storage,
User,
])

View File

@ -25,6 +25,7 @@ export type Collection<T> = {
subscribe: (f: (v: T[]) => void) => () => void
derived: (f: (v: T[]) => void) => Readable<T[]>
key: (k: string) => Key<T>
hasKey: (k: string) => boolean
getKey: (k: string) => T
setKey: (k: string, v: T) => void
mergeKey: (k: string, v: T) => void
@ -152,6 +153,7 @@ export const collection = <T>(defaults = {}): Collection<T> => {
subscribe: f => arrayStore.subscribe(f),
derived: f => derived(arrayStore, f),
key: k => key(baseStore, k),
hasKey: k => baseStore.get().has(k),
getKey: k => baseStore.get().get(k),
setKey: (k, v) => {
baseStore.update(m => {