diff --git a/packages/app/src/index.ts b/packages/app/src/index.ts index c85d700..74b328e 100644 --- a/packages/app/src/index.ts +++ b/packages/app/src/index.ts @@ -16,6 +16,7 @@ export * from './sync' export * from './tags' export * from './thunk' export * from './topics' +export * from './user' export * from './util' export * from './wot' export * from './zappers' diff --git a/packages/app/src/profiles.ts b/packages/app/src/profiles.ts index e6a8615..0aec5a4 100644 --- a/packages/app/src/profiles.ts +++ b/packages/app/src/profiles.ts @@ -9,7 +9,7 @@ import {repository, load} from './core' import {createSearch} from './util' import {collection} from './collection' import {loadRelaySelections} from './relaySelections' -import {getUserWotScore} from './wot' +import {wotGraph} from './wot' export const profiles = withGetter( deriveEventsMapped(repository, { @@ -58,8 +58,13 @@ export const profileSearch = derived(profiles, $profiles => createSearch($profiles, { onSearch: searchProfiles, getValue: (profile: PublishedProfile) => profile.event.pubkey, - sortFn: ({score, item}) => - score! < 0.1 ? dec(score!) * getUserWotScore(item.event.pubkey) : -score!, + sortFn: ({score, item}) => { + if (score && score > 0.1) return -score! + + const wotScore = wotGraph.get().get(item.event.pubkey) || 0 + + return score ? dec(score) * wotScore : -wotScore + }, fuseOptions: { keys: ["name", "display_name", {name: "about", weight: 0.3}], threshold: 0.3, diff --git a/packages/app/src/relaySelections.ts b/packages/app/src/relaySelections.ts index d0b2c63..65743bb 100644 --- a/packages/app/src/relaySelections.ts +++ b/packages/app/src/relaySelections.ts @@ -32,7 +32,7 @@ export const { load({...request, filters: [{kinds: [RELAYS], authors: [pubkey]}]}), }) -export const inboxRelaySelections = withGetter(deriveEvents(repository, {filters: [{kinds: [RELAYS]}]})) +export const inboxRelaySelections = withGetter(deriveEvents(repository, {filters: [{kinds: [INBOX_RELAYS]}]})) export const { indexStore: inboxRelaySelectionsByPubkey, diff --git a/packages/app/src/user.ts b/packages/app/src/user.ts new file mode 100644 index 0000000..1230a9b --- /dev/null +++ b/packages/app/src/user.ts @@ -0,0 +1,66 @@ +import {derived} from 'svelte/store' +import {pubkey} from './session' +import {profilesByPubkey, loadProfile} from './profiles' +import {followsByPubkey, loadFollows} from './follows' +import {mutesByPubkey, loadMutes} from './mutes' +import {relaySelectionsByPubkey, inboxRelaySelectionsByPubkey, loadRelaySelections, loadInboxRelaySelections} from './relaySelections' +import {wotGraph} from './wot' + +export const userProfile = derived( + [profilesByPubkey, pubkey], + ([$profilesByPubkey, $pubkey]) => { + if (!$pubkey) return undefined + + loadProfile($pubkey) + + return $profilesByPubkey.get($pubkey) + } +) + +export const userFollows = derived( + [followsByPubkey, pubkey], + ([$followsByPubkey, $pubkey]) => { + if (!$pubkey) return undefined + + loadFollows($pubkey) + + return $followsByPubkey.get($pubkey) + } +) + +export const userMutes = derived( + [mutesByPubkey, pubkey], + ([$mutesByPubkey, $pubkey]) => { + if (!$pubkey) return undefined + + loadMutes($pubkey) + + return $mutesByPubkey.get($pubkey) + } +) + +export const userRelaySelections = derived( + [relaySelectionsByPubkey, pubkey], + ([$relaySelectionsByPubkey, $pubkey]) => { + if (!$pubkey) return undefined + + loadRelaySelections($pubkey) + + return $relaySelectionsByPubkey.get($pubkey) + } +) + +export const userInboxRelaySelections = derived( + [inboxRelaySelectionsByPubkey, pubkey], + ([$inboxRelaySelectionsByPubkey, $pubkey]) => { + if (!$pubkey) return undefined + + loadInboxRelaySelections($pubkey) + + return $inboxRelaySelectionsByPubkey.get($pubkey) + } +) + +export const getUserWotScore = (tpk: string) => wotGraph.get().get(tpk) || 0 + +export const deriveUserWotScore = (tpk: string) => derived(wotGraph, $g => $g.get(tpk) || 0) diff --git a/packages/app/src/util.ts b/packages/app/src/util.ts index bd0cac0..c280373 100644 --- a/packages/app/src/util.ts +++ b/packages/app/src/util.ts @@ -1,6 +1,6 @@ import Fuse from "fuse.js" import type {IFuseOptions, FuseResult} from "fuse.js" -import {sortBy} from "@welshman/lib" +import {now, int, sortBy, DAY, HOUR, MINUTE} from "@welshman/lib" export type SearchOptions = { getValue: (item: T) => V @@ -24,7 +24,7 @@ export const createSearch = (options: T[], opts: SearchOptions): Sea const search = (term: string) => { opts.onSearch?.(term) - let results = term ? fuse.search(term) : options.map(item => ({item, score: 1}) as FuseResult) + let results = term ? fuse.search(term) : options.map(item => ({item}) as FuseResult) if (opts.sortFn) { results = sortBy(opts.sortFn, results) @@ -78,3 +78,27 @@ export const formatTimestampAsTime = (ts: number) => { return formatter.format(secondsToDate(ts)) } + +export const formatTimestampRelative = (ts: number) => { + let unit + let delta = now() - ts + if (delta < int(MINUTE)) { + unit = "second" + } else if (delta < int(HOUR)) { + unit = "minute" + delta = Math.round(delta / int(MINUTE)) + } else if (delta < int(DAY, 2)) { + unit = "hour" + delta = Math.round(delta / int(HOUR)) + } else { + unit = "day" + delta = Math.round(delta / int(DAY)) + } + + const locale = new Intl.RelativeTimeFormat().resolvedOptions().locale + const formatter = new Intl.RelativeTimeFormat(locale, { + numeric: "auto", + }) + + return formatter.format(-delta, unit as Intl.RelativeTimeFormatUnit) +} diff --git a/packages/app/src/wot.ts b/packages/app/src/wot.ts index ec1ea7a..736d998 100644 --- a/packages/app/src/wot.ts +++ b/packages/app/src/wot.ts @@ -83,5 +83,3 @@ export const getWotScore = (pubkey: string, target: string) => { return follows.length - mutes.length } - -export const getUserWotScore = (pubkey: string) => wotGraph.get().get(pubkey) || 0 diff --git a/packages/lib/src/Tools.ts b/packages/lib/src/Tools.ts index 758d8af..fe9d066 100644 --- a/packages/lib/src/Tools.ts +++ b/packages/lib/src/Tools.ts @@ -11,8 +11,6 @@ export type Maybe = T | undefined // Regular old utils -export const now = () => Math.round(Date.now() / 1000) - export const noop = (...args: unknown[]) => undefined export const first = (xs: T[], ...args: unknown[]) => xs[0] @@ -511,6 +509,30 @@ export const pushToMapKey = (m: Map, k: K, v: T) => { export const switcher = (k: string, m: Record) => m[k] === undefined ? m.default : m[k] +// Time + +export const MINUTE = 60 + +export const HOUR = 60 * MINUTE + +export const DAY = 24 * HOUR + +export const WEEK = 7 * DAY + +export const MONTH = 30 * DAY + +export const QUARTER = 90 * DAY + +export const YEAR = 365 * DAY + +export const int = (unit: number, count = 1) => unit * count + +export const now = () => Math.round(Date.now() / 1000) + +export const ago = (unit: number, count = 1) => now() - int(unit, count) + +export const ms = (seconds: number) => seconds * 1000 + // Fetch type FetchOpts = {