fix: safari chat regressions #264

Closed
userAdityaa wants to merge 1 commits from userAdityaa/flotilla:regression-safari into dev
3 changed files with 50 additions and 10 deletions
+12 -2
View File
@@ -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)),
Review

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?

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?
Review

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.

> 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.
)
if (matching.length > 0) {
+20 -5
View File
@@ -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}
+18 -3
View File
@@ -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>