forked from coracle/flotilla
fix: safari chat regressions
This commit is contained in:
@@ -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