forked from coracle/flotilla
Small fixes/performance improvements
This commit is contained in:
+1
-1
@@ -108,7 +108,7 @@ export const loginWithNip46 = async ({
|
||||
connectSecret?: string
|
||||
}) => {
|
||||
const broker = Nip46Broker.get({relays, clientSecret, signerPubkey})
|
||||
const result = await broker.connect(signerPubkey, connectSecret, NIP46_PERMS)
|
||||
const result = await broker.connect(connectSecret, NIP46_PERMS)
|
||||
|
||||
// TODO: remove ack result
|
||||
if (!["ack", connectSecret].includes(result)) return false
|
||||
|
||||
@@ -1,56 +1,54 @@
|
||||
<script lang="ts">
|
||||
import {onMount} from "svelte"
|
||||
import {createEditor, EditorContent} from "svelte-tiptap"
|
||||
import {writable} from "svelte/store"
|
||||
import {isMobile} from "@lib/html"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
import {getEditorOptions, getEditorTags} from "@lib/editor"
|
||||
import {getPubkeyHints} from "@app/commands"
|
||||
import {getEditor} from "@app/editor"
|
||||
|
||||
export let onSubmit: any
|
||||
export let content = ""
|
||||
export let editor = createEditor(
|
||||
getEditorOptions({
|
||||
submit,
|
||||
getPubkeyHints,
|
||||
submitOnEnter: true,
|
||||
autofocus: !isMobile,
|
||||
}),
|
||||
)
|
||||
export let editor: ReturnType<typeof getEditor> | undefined = undefined
|
||||
|
||||
function submit() {
|
||||
if ($loading) return
|
||||
const uploading = writable(false)
|
||||
|
||||
let element: HTMLElement
|
||||
|
||||
const uploadFiles = () => $editor!.chain().selectFiles().run()
|
||||
|
||||
const submit = () => {
|
||||
if ($uploading) return
|
||||
|
||||
onSubmit({
|
||||
content: $editor.getText({blockSeparator: "\n"}),
|
||||
tags: getEditorTags($editor),
|
||||
content: $editor!.getText({blockSeparator: "\n"}).trim(),
|
||||
tags: $editor!.storage.welshman.getEditorTags(),
|
||||
})
|
||||
|
||||
$editor.chain().clearContent().run()
|
||||
$editor!.chain().clearContent().run()
|
||||
}
|
||||
|
||||
$: loading = $editor?.storage.fileUpload.loading
|
||||
|
||||
onMount(() => {
|
||||
$editor.commands.setContent(content)
|
||||
editor = getEditor({autofocus: !isMobile, aggressive: true, element, submit, uploading})
|
||||
|
||||
$editor!.chain().setContent(content).run()
|
||||
})
|
||||
</script>
|
||||
|
||||
<form
|
||||
class="relative z-feature flex gap-2 p-2"
|
||||
on:submit|preventDefault={$loading ? undefined : submit}>
|
||||
on:submit|preventDefault={$uploading ? undefined : submit}>
|
||||
<Button
|
||||
data-tip="Add an image"
|
||||
class="center tooltip tooltip-right h-10 w-10 min-w-10 rounded-box bg-base-300 transition-colors hover:bg-base-200"
|
||||
disabled={$loading}
|
||||
on:click={$editor.commands.selectFiles}>
|
||||
{#if $loading}
|
||||
disabled={$uploading}
|
||||
on:click={uploadFiles}>
|
||||
{#if $uploading}
|
||||
<span class="loading loading-spinner loading-xs"></span>
|
||||
{:else}
|
||||
<Icon icon="gallery-send" />
|
||||
{/if}
|
||||
</Button>
|
||||
<div class="chat-editor flex-grow overflow-hidden">
|
||||
<EditorContent editor={$editor} />
|
||||
<div bind:this={element} />
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
<script lang="ts">
|
||||
import {onMount} from "svelte"
|
||||
import type {Readable} from "svelte/store"
|
||||
import {createEditor, type Editor, EditorContent} from "svelte-tiptap"
|
||||
import {writable} from "svelte/store"
|
||||
import {randomId} from "@welshman/lib"
|
||||
import {createEvent, EVENT_DATE, EVENT_TIME} from "@welshman/util"
|
||||
import {createEvent, EVENT_TIME} from "@welshman/util"
|
||||
import {publishThunk, dateToSeconds} from "@welshman/app"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import Field from "@lib/components/Field.svelte"
|
||||
@@ -12,16 +11,17 @@
|
||||
import ModalFooter from "@lib/components/ModalFooter.svelte"
|
||||
import DateTimeInput from "@lib/components/DateTimeInput.svelte"
|
||||
import {PROTECTED} from "@app/state"
|
||||
import {getPubkeyHints} from "@app/commands"
|
||||
import {getEditorOptions, getEditorTags} from "@lib/editor"
|
||||
import {getEditor} from "@app/editor"
|
||||
import {pushToast} from "@app/toast"
|
||||
|
||||
export let url
|
||||
|
||||
const uploading = writable(false)
|
||||
|
||||
const back = () => history.back()
|
||||
|
||||
const submit = () => {
|
||||
if ($loading) return
|
||||
if ($uploading) return
|
||||
|
||||
if (!title) {
|
||||
return pushToast({
|
||||
@@ -37,16 +37,15 @@
|
||||
})
|
||||
}
|
||||
|
||||
const kind = isAllDay ? EVENT_DATE : EVENT_TIME
|
||||
const event = createEvent(kind, {
|
||||
content: $editor.getText({blockSeparator: "\n"}),
|
||||
const event = createEvent(EVENT_TIME, {
|
||||
content: $editor.getText({blockSeparator: "\n"}).trim(),
|
||||
tags: [
|
||||
["d", randomId()],
|
||||
["title", title],
|
||||
["location", location],
|
||||
["start", dateToSeconds(start).toString()],
|
||||
["end", dateToSeconds(end).toString()],
|
||||
...getEditorTags($editor),
|
||||
...$editor.storage.welshman.getEditorTags(),
|
||||
PROTECTED,
|
||||
],
|
||||
})
|
||||
@@ -55,17 +54,15 @@
|
||||
history.back()
|
||||
}
|
||||
|
||||
let editor: Readable<Editor>
|
||||
const isAllDay = false
|
||||
let element: HTMLElement
|
||||
let editor: ReturnType<typeof getEditor>
|
||||
let title = ""
|
||||
let location = ""
|
||||
let start: Date
|
||||
let end: Date
|
||||
|
||||
$: loading = $editor?.storage.fileUpload.loading
|
||||
|
||||
onMount(() => {
|
||||
editor = createEditor(getEditorOptions({submit, getPubkeyHints}))
|
||||
editor = getEditor({submit, element, uploading})
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -86,13 +83,13 @@
|
||||
slot="input"
|
||||
class="relative z-feature flex gap-2 border-t border-solid border-base-100 bg-base-100">
|
||||
<div class="input-editor flex-grow overflow-hidden">
|
||||
<EditorContent editor={$editor} />
|
||||
<div bind:this={element} />
|
||||
</div>
|
||||
<Button
|
||||
data-tip="Add an image"
|
||||
class="center btn tooltip"
|
||||
on:click={$editor.commands.selectFiles}>
|
||||
{#if $loading}
|
||||
on:click={() => $editor.chain().selectFiles().run()}>
|
||||
{#if $uploading}
|
||||
<span class="loading loading-spinner loading-xs"></span>
|
||||
{:else}
|
||||
<Icon icon="gallery-send" />
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
import {type Instance} from "tippy.js"
|
||||
import {append, remove, uniq} from "@welshman/lib"
|
||||
import {profileSearch} from "@welshman/app"
|
||||
import {Suggestions} from "@welshman/editor"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import Tippy from "@lib/components/Tippy.svelte"
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
import Suggestions from "@lib/editor/Suggestions.svelte"
|
||||
import SuggestionProfile from "@lib/editor/SuggestionProfile.svelte"
|
||||
import ProfileSuggestion from "@app/editor/ProfileSuggestion.svelte"
|
||||
import ProfileName from "@app/components/ProfileName.svelte"
|
||||
import ProfileDetail from "@app/components/ProfileDetail.svelte"
|
||||
import {pushModal} from "@app/modal"
|
||||
@@ -78,7 +78,7 @@
|
||||
term,
|
||||
select: selectPubkey,
|
||||
search: profileSearch,
|
||||
component: SuggestionProfile,
|
||||
component: ProfileSuggestion,
|
||||
class: "rounded-box",
|
||||
style: `left: 4px; width: ${input?.clientWidth + 12}px`,
|
||||
}}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<script lang="ts">
|
||||
import {onMount} from "svelte"
|
||||
import type {Readable} from "svelte/store"
|
||||
import {createEditor, type Editor, EditorContent} from "svelte-tiptap"
|
||||
import {writable} from "svelte/store"
|
||||
import {createEvent, THREAD} from "@welshman/util"
|
||||
import {publishThunk} from "@welshman/app"
|
||||
import {isMobile} from "@lib/html"
|
||||
@@ -12,15 +11,16 @@
|
||||
import ModalFooter from "@lib/components/ModalFooter.svelte"
|
||||
import {pushToast} from "@app/toast"
|
||||
import {GENERAL, tagRoom, PROTECTED} from "@app/state"
|
||||
import {getPubkeyHints} from "@app/commands"
|
||||
import {getEditorOptions, getEditorTags} from "@lib/editor"
|
||||
import {getEditor} from "@app/editor"
|
||||
|
||||
export let url
|
||||
|
||||
const uploading = writable(false)
|
||||
|
||||
const back = () => history.back()
|
||||
|
||||
const submit = () => {
|
||||
if ($loading) return
|
||||
if ($uploading) return
|
||||
|
||||
if (!title) {
|
||||
return pushToast({
|
||||
@@ -29,7 +29,7 @@
|
||||
})
|
||||
}
|
||||
|
||||
const content = $editor.getText({blockSeparator: "\n"})
|
||||
const content = $editor.getText({blockSeparator: "\n"}).trim()
|
||||
|
||||
if (!content.trim()) {
|
||||
return pushToast({
|
||||
@@ -38,7 +38,12 @@
|
||||
})
|
||||
}
|
||||
|
||||
const tags = [["title", title], tagRoom(GENERAL, url), ...getEditorTags($editor), PROTECTED]
|
||||
const tags = [
|
||||
...$editor.storage.welshman.getEditorTags(),
|
||||
tagRoom(GENERAL, url),
|
||||
["title", title],
|
||||
PROTECTED,
|
||||
]
|
||||
|
||||
publishThunk({
|
||||
relays: [url],
|
||||
@@ -49,14 +54,11 @@
|
||||
}
|
||||
|
||||
let title: string
|
||||
let editor: Readable<Editor>
|
||||
|
||||
$: loading = $editor?.storage.fileUpload.loading
|
||||
let element: HTMLElement
|
||||
let editor: ReturnType<typeof getEditor>
|
||||
|
||||
onMount(() => {
|
||||
editor = createEditor(
|
||||
getEditorOptions({submit, getPubkeyHints, placeholder: "What's on your mind?"}),
|
||||
)
|
||||
editor = getEditor({submit, element, uploading, placeholder: "What's on your mind?"})
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -81,14 +83,14 @@
|
||||
<Field>
|
||||
<p slot="label">Message*</p>
|
||||
<div slot="input" class="note-editor flex-grow overflow-hidden">
|
||||
<EditorContent editor={$editor} />
|
||||
<div bind:this={element} />
|
||||
</div>
|
||||
</Field>
|
||||
<Button
|
||||
data-tip="Add an image"
|
||||
class="tooltip tooltip-left absolute bottom-1 right-2"
|
||||
on:click={$editor.commands.selectFiles}>
|
||||
{#if $loading}
|
||||
{#if $uploading}
|
||||
<span class="loading loading-spinner loading-xs"></span>
|
||||
{:else}
|
||||
<Icon icon="paperclip" size={3} />
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
<script lang="ts">
|
||||
import {onMount} from "svelte"
|
||||
import type {Readable} from "svelte/store"
|
||||
import {createEditor, type Editor, EditorContent} from "svelte-tiptap"
|
||||
import {writable} from "svelte/store"
|
||||
import {isMobile} from "@lib/html"
|
||||
import {fly, slideAndFade} from "@lib/transition"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
import ModalFooter from "@lib/components/ModalFooter.svelte"
|
||||
import {getEditorOptions, getEditorTags} from "@lib/editor"
|
||||
import {getPubkeyHints, publishComment} from "@app/commands"
|
||||
import {publishComment} from "@app/commands"
|
||||
import {tagRoom, GENERAL, PROTECTED} from "@app/state"
|
||||
import {getEditor} from "@app/editor"
|
||||
import {pushToast} from "@app/toast"
|
||||
|
||||
export let url
|
||||
@@ -17,13 +16,15 @@
|
||||
export let onClose
|
||||
export let onSubmit
|
||||
|
||||
const uploading = writable(false)
|
||||
|
||||
const submit = () => {
|
||||
if ($loading) return
|
||||
if ($uploading) return
|
||||
|
||||
const content = $editor.getText({blockSeparator: "\n"})
|
||||
const tags = [...getEditorTags($editor), tagRoom(GENERAL, url), PROTECTED]
|
||||
const content = $editor.getText({blockSeparator: "\n"}).trim()
|
||||
const tags = [...$editor.storage.welshman.getEditorTags(), tagRoom(GENERAL, url), PROTECTED]
|
||||
|
||||
if (!content.trim()) {
|
||||
if (!content) {
|
||||
return pushToast({
|
||||
theme: "error",
|
||||
message: "Please provide a message for your reply.",
|
||||
@@ -33,12 +34,11 @@
|
||||
onSubmit(publishComment({event, content, tags, relays: [url]}))
|
||||
}
|
||||
|
||||
let editor: Readable<Editor>
|
||||
|
||||
$: loading = $editor?.storage.fileUpload.loading
|
||||
let editor: ReturnType<typeof getEditor>
|
||||
let element: HTMLElement
|
||||
|
||||
onMount(() => {
|
||||
editor = createEditor(getEditorOptions({submit, getPubkeyHints, autofocus: !isMobile}))
|
||||
editor = getEditor({element, submit, uploading, autofocus: !isMobile})
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -49,13 +49,13 @@
|
||||
class="card2 sticky bottom-2 z-feature mx-2 mt-4 bg-neutral">
|
||||
<div class="relative">
|
||||
<div class="note-editor flex-grow overflow-hidden">
|
||||
<EditorContent editor={$editor} />
|
||||
<div bind:this={element} />
|
||||
</div>
|
||||
<Button
|
||||
data-tip="Add an image"
|
||||
class="tooltip tooltip-left absolute bottom-1 right-2"
|
||||
on:click={$editor.commands.selectFiles}>
|
||||
{#if $loading}
|
||||
{#if $uploading}
|
||||
<span class="loading loading-spinner loading-xs"></span>
|
||||
{:else}
|
||||
<Icon icon="paperclip" size={3} />
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
<script lang="ts">
|
||||
import type {NodeViewProps} from "@tiptap/core"
|
||||
import {NodeViewWrapper} from "svelte-tiptap"
|
||||
import {deriveProfileDisplay} from "@welshman/app"
|
||||
|
||||
export let node: NodeViewProps["node"]
|
||||
export let selected: NodeViewProps["selected"]
|
||||
|
||||
const display = deriveProfileDisplay(node.attrs.pubkey)
|
||||
</script>
|
||||
|
||||
<NodeViewWrapper as="span">
|
||||
<button class="tiptap-object {selected ? 'tiptap-active' : ''}">
|
||||
@{$display}
|
||||
</button>
|
||||
</NodeViewWrapper>
|
||||
@@ -0,0 +1,38 @@
|
||||
<script lang="ts">
|
||||
import {displayPubkey, getPubkeyTagValues, getListTags} from "@welshman/util"
|
||||
import {
|
||||
userFollows,
|
||||
deriveUserWotScore,
|
||||
deriveHandleForPubkey,
|
||||
displayHandle,
|
||||
deriveProfileDisplay,
|
||||
} from "@welshman/app"
|
||||
import WotScore from "@lib/components/WotScore.svelte"
|
||||
import ProfileCircle from "@app/components/ProfileCircle.svelte"
|
||||
|
||||
export let value
|
||||
|
||||
const pubkey = value
|
||||
const profileDisplay = deriveProfileDisplay(pubkey)
|
||||
const handle = deriveHandleForPubkey(pubkey)
|
||||
const score = deriveUserWotScore(pubkey)
|
||||
|
||||
$: following = getPubkeyTagValues(getListTags($userFollows)).includes(pubkey)
|
||||
</script>
|
||||
|
||||
<div class="flex max-w-full gap-3">
|
||||
<div class="py-1">
|
||||
<ProfileCircle {pubkey} />
|
||||
</div>
|
||||
<div class="flex min-w-0 flex-col">
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="text-bold overflow-hidden text-ellipsis text-base">
|
||||
{$profileDisplay}
|
||||
</div>
|
||||
<WotScore score={$score} active={following} />
|
||||
</div>
|
||||
<div class="overflow-hidden text-ellipsis text-sm opacity-75">
|
||||
{$handle ? displayHandle($handle) : displayPubkey(pubkey)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,102 @@
|
||||
import type {Writable} from "svelte/store"
|
||||
import {derived} from "svelte/store"
|
||||
import {createEditor, SvelteNodeViewRenderer} from "svelte-tiptap"
|
||||
import {ctx} from "@welshman/lib"
|
||||
import type {StampedEvent} from "@welshman/util"
|
||||
import {signer, profileSearch} from "@welshman/app"
|
||||
import {MentionSuggestion, WelshmanExtension} from "@welshman/editor"
|
||||
import {getSetting, userSettingValues} from "@app/state"
|
||||
import ProfileSuggestion from "./ProfileSuggestion.svelte"
|
||||
import EditMention from "./EditMention.svelte"
|
||||
|
||||
export const getUploadType = () => getSetting<"nip96" | "blossom">("upload_type")
|
||||
|
||||
export const getUploadUrl = () => {
|
||||
const {upload_type, nip96_urls, blossom_urls} = userSettingValues.get()
|
||||
|
||||
return upload_type === "nip96"
|
||||
? nip96_urls[0] || "https://nostr.build"
|
||||
: blossom_urls[0] || "https://cdn.satellite.earth"
|
||||
}
|
||||
|
||||
export const signWithAssert = async (template: StampedEvent) => {
|
||||
const event = await signer.get().sign(template)
|
||||
|
||||
return event!
|
||||
}
|
||||
|
||||
export const getEditor = ({
|
||||
aggressive = false,
|
||||
autofocus = false,
|
||||
charCount,
|
||||
content = "",
|
||||
element,
|
||||
placeholder = "",
|
||||
submit,
|
||||
uploading,
|
||||
wordCount,
|
||||
}: {
|
||||
aggressive?: boolean
|
||||
autofocus?: boolean
|
||||
charCount?: Writable<number>
|
||||
content?: string
|
||||
element: HTMLElement
|
||||
placeholder?: string
|
||||
submit: () => void
|
||||
uploading?: Writable<boolean>
|
||||
wordCount?: Writable<number>
|
||||
}) =>
|
||||
createEditor({
|
||||
element,
|
||||
content,
|
||||
autofocus,
|
||||
extensions: [
|
||||
WelshmanExtension.configure({
|
||||
submit,
|
||||
sign: signWithAssert,
|
||||
defaultUploadType: getUploadType(),
|
||||
defaultUploadUrl: getUploadUrl(),
|
||||
extensions: {
|
||||
placeholder: {
|
||||
config: {
|
||||
placeholder,
|
||||
},
|
||||
},
|
||||
breakOrSubmit: {
|
||||
config: {
|
||||
aggressive,
|
||||
},
|
||||
},
|
||||
fileUpload: {
|
||||
config: {
|
||||
onDrop() {
|
||||
uploading?.set(true)
|
||||
},
|
||||
onComplete() {
|
||||
uploading?.set(false)
|
||||
},
|
||||
},
|
||||
},
|
||||
nprofile: {
|
||||
extend: {
|
||||
addNodeView: () => SvelteNodeViewRenderer(EditMention),
|
||||
addProseMirrorPlugins() {
|
||||
return [
|
||||
MentionSuggestion({
|
||||
editor: (this as any).editor,
|
||||
search: derived(profileSearch, s => s.searchValues),
|
||||
getRelays: (pubkey: string) => ctx.app.router.FromPubkeys([pubkey]).getUrls(),
|
||||
component: ProfileSuggestion,
|
||||
}),
|
||||
]
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
],
|
||||
onUpdate({editor}) {
|
||||
wordCount?.set(editor.storage.wordCount.words)
|
||||
charCount?.set(editor.storage.wordCount.chars)
|
||||
},
|
||||
})
|
||||
+20
-15
@@ -1,7 +1,7 @@
|
||||
import {derived} from "svelte/store"
|
||||
import {synced} from "@welshman/store"
|
||||
import {synced, throttled} from "@welshman/store"
|
||||
import {pubkey} from "@welshman/app"
|
||||
import {prop, sortBy, now} from "@welshman/lib"
|
||||
import {prop, identity, now} from "@welshman/lib"
|
||||
import type {TrustedEvent} from "@welshman/util"
|
||||
import {MESSAGE} from "@welshman/util"
|
||||
import {makeSpacePath, makeChatPath, makeThreadPath, makeRoomPath} from "@app/routes"
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
THREAD_FILTER,
|
||||
COMMENT_FILTER,
|
||||
chats,
|
||||
getEventsForUrl,
|
||||
getUrlsForEvent,
|
||||
userRoomsByUrl,
|
||||
repositoryStore,
|
||||
} from "@app/state"
|
||||
@@ -25,11 +25,12 @@ export const setChecked = (key: string) => checked.update(state => ({...state, [
|
||||
// Derived notifications state
|
||||
|
||||
export const notifications = derived(
|
||||
[pubkey, checked, chats, userRoomsByUrl, repositoryStore],
|
||||
([$pubkey, $checked, $chats, $userRoomsByUrl, $repository]) => {
|
||||
const hasNotification = (path: string, events: TrustedEvent[]) => {
|
||||
const [latestEvent] = sortBy($e => -$e.created_at, events)
|
||||
|
||||
throttled(
|
||||
1000,
|
||||
derived([pubkey, checked, chats, userRoomsByUrl, repositoryStore, getUrlsForEvent], identity),
|
||||
),
|
||||
([$pubkey, $checked, $chats, $userRoomsByUrl, $repository, $getUrlsForEvent]) => {
|
||||
const hasNotification = (path: string, latestEvent: TrustedEvent | undefined) => {
|
||||
if (!latestEvent || latestEvent.pubkey === $pubkey) {
|
||||
return false
|
||||
}
|
||||
@@ -50,29 +51,33 @@ export const notifications = derived(
|
||||
for (const {pubkeys, messages} of $chats) {
|
||||
const chatPath = makeChatPath(pubkeys)
|
||||
|
||||
if (hasNotification(chatPath, messages)) {
|
||||
if (hasNotification(chatPath, messages[0])) {
|
||||
paths.add("/chat")
|
||||
paths.add(chatPath)
|
||||
}
|
||||
}
|
||||
|
||||
const allThreadEvents = $repository.query([THREAD_FILTER, COMMENT_FILTER])
|
||||
const allMessageEvents = $repository.query([{kinds: [MESSAGE]}])
|
||||
|
||||
for (const [url, rooms] of $userRoomsByUrl.entries()) {
|
||||
const spacePath = makeSpacePath(url)
|
||||
const threadPath = makeThreadPath(url)
|
||||
const threadFilters = [THREAD_FILTER, COMMENT_FILTER]
|
||||
const threadEvents = getEventsForUrl($repository, url, threadFilters)
|
||||
const latestEvent = allThreadEvents.find(e => $getUrlsForEvent(e.id).includes(url))
|
||||
|
||||
if (hasNotification(threadPath, threadEvents)) {
|
||||
if (hasNotification(threadPath, latestEvent)) {
|
||||
paths.add(spacePath)
|
||||
paths.add(threadPath)
|
||||
}
|
||||
|
||||
for (const room of rooms) {
|
||||
const roomPath = makeRoomPath(url, room)
|
||||
const roomFilters = [{kinds: [MESSAGE], "#h": [room]}]
|
||||
const roomEvents = getEventsForUrl($repository, url, roomFilters)
|
||||
const latestEvent = allMessageEvents.find(
|
||||
e =>
|
||||
$getUrlsForEvent(e.id).includes(url) && e.tags.find(t => t[0] === "h" && t[1] === room),
|
||||
)
|
||||
|
||||
if (hasNotification(roomPath, roomEvents)) {
|
||||
if (hasNotification(roomPath, latestEvent)) {
|
||||
paths.add(spacePath)
|
||||
paths.add(roomPath)
|
||||
}
|
||||
|
||||
+5
-2
@@ -1,20 +1,23 @@
|
||||
import {get} from "svelte/store"
|
||||
import {partition, assoc, now, ago, MONTH} from "@welshman/lib"
|
||||
import {MESSAGE, DELETE, THREAD, COMMENT} from "@welshman/util"
|
||||
import type {Subscription} from "@welshman/net"
|
||||
import type {AppSyncOpts} from "@welshman/app"
|
||||
import {subscribe, repository, pull, hasNegentropy} from "@welshman/app"
|
||||
import {userRoomsByUrl, getEventsForUrl} from "@app/state"
|
||||
import {userRoomsByUrl, getUrlsForEvent} from "@app/state"
|
||||
|
||||
// Utils
|
||||
|
||||
export const pullConservatively = ({relays, filters}: AppSyncOpts) => {
|
||||
const $getUrlsForEvent = get(getUrlsForEvent)
|
||||
const [smart, dumb] = partition(hasNegentropy, relays)
|
||||
const promises = [pull({relays: smart, filters})]
|
||||
const allEvents = repository.query(filters, {shouldSort: false})
|
||||
|
||||
// Since pulling from relays without negentropy is expensive, limit how many
|
||||
// duplicates we repeatedly download
|
||||
for (const url of dumb) {
|
||||
const events = getEventsForUrl(repository, url, filters)
|
||||
const events = allEvents.filter(e => $getUrlsForEvent(e.id).includes(url))
|
||||
|
||||
if (events.length > 100) {
|
||||
filters = filters.map(assoc("since", events[10]!.created_at))
|
||||
|
||||
+2
-1
@@ -607,7 +607,8 @@ export const userSettingValues = withGetter(
|
||||
derived(userSettings, $s => $s?.values || defaultSettings),
|
||||
)
|
||||
|
||||
export const getSetting = (key: keyof Settings["values"]) => userSettingValues.get()[key]
|
||||
export const getSetting = <T = any>(key: keyof Settings["values"]) =>
|
||||
userSettingValues.get()[key] as T
|
||||
|
||||
export const userMembership = withGetter(
|
||||
derived([pubkey, membershipByPubkey], ([$pubkey, $membershipByPubkey]) => {
|
||||
|
||||
Reference in New Issue
Block a user