fix: safari chat regressions #264
@@ -28,7 +28,7 @@ import {
|
||||
import type {TrustedEvent, Filter, List} from "@welshman/util"
|
||||
import {load, request, mergeRepositoryUpdates} from "@welshman/net"
|
||||
import type {RepositoryUpdate} from "@welshman/net"
|
||||
import {repository, loadRelay, tracker} from "@welshman/app"
|
||||
import {pubkey, repository, loadRelay, tracker} from "@welshman/app"
|
||||
import {createScroller} from "@lib/html"
|
||||
import {daysBetween} from "@lib/util"
|
||||
import {getEventsForUrl} from "@app/core/state"
|
||||
@@ -41,6 +41,7 @@ export const makeFeed = ({
|
||||
element,
|
||||
onBackwardExhausted,
|
||||
onForwardExhausted,
|
||||
allowOptimisticSelfEvents = false,
|
||||
at = now(),
|
||||
}: {
|
||||
url: string
|
||||
@@ -48,6 +49,7 @@ export const makeFeed = ({
|
||||
element: HTMLElement
|
||||
onBackwardExhausted?: () => void
|
||||
onForwardExhausted?: () => void
|
||||
allowOptimisticSelfEvents?: boolean
|
||||
at?: number
|
||||
}) => {
|
||||
const controller = new AbortController()
|
||||
@@ -113,7 +115,15 @@ export const makeFeed = ({
|
||||
}
|
||||
|
||||
const matching = added.filter(
|
||||
event => matchFilters(filters, event) && tracker.getRelays(event.id).has(url),
|
||||
event =>
|
||||
matchFilters(filters, event) &&
|
||||
(tracker.getRelays(event.id).has(url) ||
|
||||
// In Safari, relay confirmation can lag behind local repository updates.
|
||||
// Only enable this for chat-like feeds that explicitly opt in.
|
||||
(allowOptimisticSelfEvents &&
|
||||
event.pubkey === pubkey.get() &&
|
||||
tracker.getRelays(event.id).size === 0 &&
|
||||
event.created_at >= now() - 60)),
|
||||
|
|
||||
)
|
||||
|
||||
if (matching.length > 0) {
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
import InfoCircle from "@assets/icons/info-circle.svg?dataurl"
|
||||
import Login2 from "@assets/icons/login-3.svg?dataurl"
|
||||
import cx from "classnames"
|
||||
import {slide, fade, fly} from "@lib/transition"
|
||||
import {fade, fly} from "@lib/transition"
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
import Divider from "@lib/components/Divider.svelte"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
@@ -246,6 +246,7 @@
|
||||
if (!isProgrammaticScroll) {
|
||||
userHasScrolled = true
|
||||
isUserScrolling = true
|
||||
wasAtBottom = !element || Math.abs(element.scrollTop) < 100
|
||||
clearIsUserScrolling()
|
||||
manageScrollPosition()
|
||||
}
|
||||
@@ -281,6 +282,7 @@
|
||||
let events: Readable<TrustedEvent[]> = $state(readable([]))
|
||||
let compose: RoomCompose | undefined = $state()
|
||||
let eventToEdit: TrustedEvent | undefined = $state()
|
||||
let wasAtBottom = true
|
||||
|
||||
const clearIsUserScrolling = debounce(150, () => {
|
||||
isUserScrolling = false
|
||||
@@ -359,8 +361,20 @@
|
||||
})
|
||||
|
||||
$effect(() => {
|
||||
if (elements.length > 0 && !isUserScrolling) {
|
||||
requestAnimationFrame(manageScrollPosition)
|
||||
if (elements.length > 0) {
|
||||
requestAnimationFrame(() => {
|
||||
// Safari does not implement CSS scroll anchoring for flex-col-reverse.
|
||||
// When a new message is inserted, Safari shifts scrollTop upward to preserve
|
||||
// visual position rather than keeping it pinned at 0 (visual bottom).
|
||||
// Snap back to 0 whenever the user was at the bottom before the update.
|
||||
if (element && isNaN(at) && wasAtBottom) {
|
||||
isProgrammaticScroll = true
|
||||
element.scrollTop = 0
|
||||
}
|
||||
if (!isUserScrolling) {
|
||||
manageScrollPosition()
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
@@ -371,6 +385,7 @@
|
||||
url,
|
||||
at: at || now(),
|
||||
element: element!,
|
||||
allowOptimisticSelfEvents: true,
|
||||
filters: [{kinds: [MESSAGE, ROOM_ADD_MEMBER], "#h": [h]}],
|
||||
onBackwardExhausted: () => {
|
||||
loadingBackward = false
|
||||
@@ -404,7 +419,7 @@
|
||||
onMount(() => {
|
||||
start()
|
||||
|
||||
return cleanup
|
||||
return () => cleanup?.()
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -496,7 +511,7 @@
|
||||
{#if event.kind === ROOM_ADD_MEMBER}
|
||||
<RoomItemAddMember {url} {event} />
|
||||
{:else}
|
||||
<div in:slide class="cv">
|
||||
<div>
|
||||
<RoomItem
|
||||
{url}
|
||||
{event}
|
||||
|
||||
@@ -141,6 +141,7 @@
|
||||
if (!isProgrammaticScroll) {
|
||||
userHasScrolled = true
|
||||
isUserScrolling = true
|
||||
wasAtBottom = !element || Math.abs(element.scrollTop) < 100
|
||||
clearIsUserScrolling()
|
||||
manageScrollPosition()
|
||||
}
|
||||
@@ -174,6 +175,7 @@
|
||||
let events: Readable<TrustedEvent[]> = $state(readable([]))
|
||||
let compose: RoomCompose | undefined = $state()
|
||||
let eventToEdit: TrustedEvent | undefined = $state()
|
||||
let wasAtBottom = true
|
||||
|
||||
const clearIsUserScrolling = debounce(150, () => {
|
||||
isUserScrolling = false
|
||||
@@ -252,8 +254,20 @@
|
||||
})
|
||||
|
||||
$effect(() => {
|
||||
if (elements.length > 0 && !isUserScrolling) {
|
||||
requestAnimationFrame(manageScrollPosition)
|
||||
if (elements.length > 0) {
|
||||
requestAnimationFrame(() => {
|
||||
// Safari does not implement CSS scroll anchoring for flex-col-reverse.
|
||||
// When a new message is inserted, Safari shifts scrollTop upward to preserve
|
||||
// visual position rather than keeping it pinned at 0 (visual bottom).
|
||||
// Snap back to 0 whenever the user was at the bottom before the update.
|
||||
if (element && isNaN(at) && wasAtBottom) {
|
||||
isProgrammaticScroll = true
|
||||
element.scrollTop = 0
|
||||
}
|
||||
if (!isUserScrolling) {
|
||||
manageScrollPosition()
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
@@ -264,6 +278,7 @@
|
||||
url,
|
||||
at: at || now(),
|
||||
element: element!,
|
||||
allowOptimisticSelfEvents: true,
|
||||
filters: [{kinds: [MESSAGE, RELAY_ADD_MEMBER]}],
|
||||
onBackwardExhausted: () => {
|
||||
loadingBackward = false
|
||||
@@ -297,7 +312,7 @@
|
||||
onMount(() => {
|
||||
start()
|
||||
|
||||
return cleanup
|
||||
return () => cleanup?.()
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user
This should be handled by welshman's thunk stuff (when a thunk is enqueued it optimistically tracks it at all target relays). Are you seeing new events not showing up immediately?
Not consistently. It’s an intermittent Safari timing issue, so new events can appear immediately most of the time, but occasionally show up late (especially right after send) until relay confirmation arrives.
This issue doesn’t occur in browsers like Chrome or Firefox, which I’ve also tested.