Compare commits

..

10 Commits

Author SHA1 Message Date
Jon Staab 53954aae89 classnames tweak 2026-04-17 16:06:00 -07:00
userAdityaa 24aa62a503 chore: carify Pomade login errors with actionable invalid vs network messaging (#233)
Co-authored-by: userAdityaa <aditya.chaudhary1558@gmail.com>
Co-committed-by: userAdityaa <aditya.chaudhary1558@gmail.com>
2026-04-17 23:00:03 +00:00
Jon Staab 2618bb9c63 Fix centered layout 2026-04-17 14:58:59 -07:00
Prat_09 32a31045ef fix: Improve toggle switch placement in settings screen (#208) (#232)
Co-authored-by: Pratyush Mohanty <prat_09@noreply.coracle.social>
Co-committed-by: Pratyush Mohanty <prat_09@noreply.coracle.social>
2026-04-17 21:58:52 +00:00
DeveshSingh 56edad77a8 fix: added logic for password requirements on signup (#230)
Co-authored-by: deveshanim3 <deveshsingh6986@gmail.com>
Co-committed-by: deveshanim3 <deveshsingh6986@gmail.com>
2026-04-17 19:43:27 +00:00
priyanshu_bharti fdb604e350 Use type=email for signup/login email inputs (#225) (#228)
Co-authored-by: Priyanshubhartistm <bhartipriyanshustm@gmail.com>
Co-committed-by: Priyanshubhartistm <bhartipriyanshustm@gmail.com>
2026-04-17 18:55:04 +00:00
DeveshSingh 3c66dfd83c fix/wrong-message-offline (#222)
Co-authored-by: deveshanim3 <deveshsingh6986@gmail.com>
Co-committed-by: deveshanim3 <deveshsingh6986@gmail.com>
2026-04-17 18:24:55 +00:00
userAdityaa 81633b0a1e fix: vertical alignment of emoji and overflow buttons in shared event action row (#219)
Co-authored-by: userAdityaa <aditya.chaudhary1558@gmail.com>
Co-committed-by: userAdityaa <aditya.chaudhary1558@gmail.com>
2026-04-17 15:22:40 +00:00
Khushvendra 4a967de184 fix(chat): suppress programmatic scroll while user is scrolling (#132) (#216)
Co-authored-by: 1amKhush <khushvendras99@gmail.com>
Co-committed-by: 1amKhush <khushvendras99@gmail.com>
2026-04-16 23:20:17 +00:00
DeveshSingh 59961cbdb5 fix: supported nip overflow in SpaceRelayStatus.svelte (#215)
Co-authored-by: deveshanim3 <deveshsingh6986@gmail.com>
Co-committed-by: deveshanim3 <deveshsingh6986@gmail.com>
2026-04-16 21:36:14 +00:00
19 changed files with 257 additions and 98 deletions
+3 -2
View File
@@ -42,7 +42,7 @@
let popover: Instance | undefined = $state()
</script>
<Button class="join rounded-full">
<div class="join items-center rounded-full">
{#if ENABLE_ZAPS && !hideZap}
<ZapButton {url} {event} class="btn join-item btn-neutral btn-xs">
<Icon icon={Bolt} size={4} />
@@ -52,6 +52,7 @@
<Icon icon={SmileCircle} size={4} />
</EmojiButton>
<Tippy
class="flex"
bind:popover
component={EventMenu}
props={{url, noun, event, customActions, onClick: hidePopover}}
@@ -60,4 +61,4 @@
<Icon icon={MenuDots} size={4} />
</Button>
</Tippy>
</Button>
</div>
+11 -3
View File
@@ -19,6 +19,7 @@
import LogInOTP from "@app/components/LogInOTP.svelte"
import LogInSelect from "@app/components/LogInSelect.svelte"
import {deleteDeactivatedPomadeSessions, loginWithPomade} from "@app/util/pomade"
import {getPomadeLoginFailureMessage, POMADE_NETWORK_ERROR_MESSAGE} from "@app/util/pomadeErrors"
import {pushModal, clearModals} from "@app/util/modal"
import {setChecked} from "@app/util/notifications"
import {pushToast} from "@app/util/toast"
@@ -44,7 +45,7 @@
return pushToast({
theme: "error",
message: "Sorry, we were unable to log you in.",
message: getPomadeLoginFailureMessage(messages),
})
}
@@ -64,10 +65,17 @@
pushToast({
theme: "error",
message: "Sorry, we were unable to log you in.",
message: getPomadeLoginFailureMessage(res.messages),
})
}
}
} catch (error) {
console.error("Login error:", error)
pushToast({
theme: "error",
message: POMADE_NETWORK_ERROR_MESSAGE,
})
} finally {
loading = false
}
@@ -90,7 +98,7 @@
{#snippet input()}
<label class="input input-bordered flex w-full items-center gap-2">
<Icon icon={Letter} />
<input bind:value={email} />
<input type="email" bind:value={email} />
</label>
{/snippet}
</FieldInline>
+12 -2
View File
@@ -15,6 +15,7 @@
import ModalSubtitle from "@lib/components/ModalSubtitle.svelte"
import ModalFooter from "@lib/components/ModalFooter.svelte"
import LogInOTPConfirm from "@app/components/LogInOTPConfirm.svelte"
import {POMADE_NETWORK_ERROR_MESSAGE} from "@app/util/pomadeErrors"
import {pushModal} from "@app/util/modal"
import {pushToast} from "@app/util/toast"
@@ -35,11 +36,20 @@
if (ok) {
pushModal(LogInOTPConfirm, {email, peersByPrefix})
} else {
console.error("Pomade challenge request failed during OTP login")
pushToast({
theme: "error",
message: "Sorry, we were unable to request a login code.",
message: POMADE_NETWORK_ERROR_MESSAGE,
})
}
} catch (error) {
console.error(error)
pushToast({
theme: "error",
message: POMADE_NETWORK_ERROR_MESSAGE,
})
} finally {
loading = false
}
@@ -61,7 +71,7 @@
{#snippet input()}
<label class="input input-bordered flex w-full items-center gap-2">
<Icon icon={Letter} />
<input bind:value={email} />
<input type="email" bind:value={email} />
</label>
{/snippet}
</FieldInline>
+12 -4
View File
@@ -15,10 +15,11 @@
import ModalFooter from "@lib/components/ModalFooter.svelte"
import StringMultiInput from "@lib/components/StringMultiInput.svelte"
import LogInSelect from "@app/components/LogInSelect.svelte"
import {pushToast} from "@app/util/toast"
import {setChecked} from "@app/util/notifications"
import {pushModal, clearModals} from "@app/util/modal"
import {setChecked} from "@app/util/notifications"
import {deleteDeactivatedPomadeSessions, loginWithPomade} from "@app/util/pomade"
import {getPomadeLoginFailureMessage, POMADE_NETWORK_ERROR_MESSAGE} from "@app/util/pomadeErrors"
import {pushToast} from "@app/util/toast"
type Props = {
email: string
@@ -44,7 +45,7 @@
return pushToast({
theme: "error",
message: "Sorry, we were unable to log you in.",
message: getPomadeLoginFailureMessage(messages),
})
}
@@ -64,10 +65,17 @@
pushToast({
theme: "error",
message: "Sorry, we were unable to log you in.",
message: getPomadeLoginFailureMessage(res.messages),
})
}
}
} catch (error) {
console.error("Login error:", error)
pushToast({
theme: "error",
message: POMADE_NETWORK_ERROR_MESSAGE,
})
} finally {
loading = false
}
+9 -1
View File
@@ -14,6 +14,7 @@
import ModalFooter from "@lib/components/ModalFooter.svelte"
import Profile from "@app/components/Profile.svelte"
import {deleteDeactivatedPomadeSessions, loginWithPomade} from "@app/util/pomade"
import {getPomadeLoginFailureMessage, POMADE_NETWORK_ERROR_MESSAGE} from "@app/util/pomadeErrors"
import {setChecked} from "@app/util/notifications"
import {clearModals} from "@app/util/modal"
import {pushToast} from "@app/util/toast"
@@ -46,9 +47,16 @@
pushToast({
theme: "error",
message: "Sorry, we were unable to log you in.",
message: getPomadeLoginFailureMessage(res.messages),
})
}
} catch (error) {
console.error("Login error:", error)
pushToast({
theme: "error",
message: POMADE_NETWORK_ERROR_MESSAGE,
})
} finally {
loading = false
}
+4 -1
View File
@@ -120,7 +120,7 @@
{#snippet input()}
<label class="input input-bordered flex w-full items-center gap-2">
<Icon icon={Letter} />
<input bind:value={email} />
<input type="email" bind:value={email} />
</label>
{/snippet}
</FieldInline>
@@ -134,6 +134,9 @@
<input type="password" bind:value={password} />
</label>
{/snippet}
{#snippet info()}
Must be at least 12 characters long.
{/snippet}
</FieldInline>
</ModalBody>
<ModalFooter>
+22 -2
View File
@@ -25,6 +25,16 @@
const {url} = $props()
const authError = deriveRelayAuthError(url)
let networkError = $state(false)
const isExplicitAuthError = $derived(
$authError &&
!(
$authError.toLowerCase().includes("failed") ||
$authError.toLowerCase().includes("timeout") ||
$authError.toLowerCase().includes("network")
),
)
const isGenericError = $derived(networkError || ($authError && !isExplicitAuthError))
const back = () => history.back()
const copyInvite = () => clip(invite)
@@ -70,8 +80,14 @@
])
claim = getTagValue("claim", event?.tags || []) || ""
} catch {
} catch (err) {
claim = ""
if (
(err instanceof Error && (err.name === "AbortError" || err.name === "TimeoutError")) ||
!navigator.onLine
) {
networkError = true
}
} finally {
loading = false
}
@@ -92,7 +108,11 @@
<p class="center">
<Spinner {loading}>Requesting an invite link...</Spinner>
</p>
{:else if $authError}
{:else if isGenericError}
<p class="center text-center">
Unable to reach the relay. Please check your connection and try again.
</p>
{:else if isExplicitAuthError}
<p class="center">Oops! It looks like you're not a member of this relay.</p>
{:else}
<div class="flex flex-col items-center gap-6">
+1 -1
View File
@@ -46,7 +46,7 @@
</div>
{/if}
{#if Array.isArray(supported_nips)}
<p class="badge badge-neutral">
<p class="badge badge-neutral text-wrap h-auto">
<span class="ellipsize">Supported NIPs: {supported_nips.join(", ")}</span>
</p>
{/if}
+11
View File
@@ -0,0 +1,11 @@
export const POMADE_INVALID_LOGIN_MESSAGE = "Invalid login information"
export const POMADE_NETWORK_ERROR_MESSAGE = "Network error, please try again"
type PomadeMessage = {
res?: unknown
}
export const getPomadeLoginFailureMessage = (messages: PomadeMessage[]) =>
messages.some(message => message.res !== undefined)
? POMADE_INVALID_LOGIN_MESSAGE
: POMADE_NETWORK_ERROR_MESSAGE
+1
View File
@@ -31,6 +31,7 @@
<svelte:document onmousemove={onMouseMove} />
<Tippy
class="flex"
bind:popover
component={EmojiPicker}
props={{onClick}}
+17 -11
View File
@@ -9,16 +9,22 @@
const {...props}: Props = $props()
</script>
<div class="grid grid-cols-1 gap-2 lg:gap-6 lg:grid-cols-3 {props.class}">
<label class="flex items-center gap-2 font-bold">
{@render props.label?.()}
</label>
<div class="col-span-2 flex items-center gap-2">
{@render props.input?.()}
</div>
<p class="flex-end text-sm opacity-50 lg:col-span-3">
{#if props.info}
{@render props.info?.()}
<div class="flex flex-col gap-2 {props.class}">
<div class="flex items-center justify-between w-full gap-2">
{#if props.label}
<label class="flex items-center gap-2 max-w-[80%] md:max-w-none">
{@render props.label()}
</label>
{/if}
</p>
<div class="flex items-center gap-2 justify-end shrink-0">
{#if props.input}
{@render props.input()}
{/if}
</div>
</div>
{#if props.info}
<p class="text-sm opacity-50">
{@render props.info()}
</p>
{/if}
</div>
+16 -9
View File
@@ -13,7 +13,12 @@
placeholder?: string
}
let {value = $bindable(), addLabel, placeholder = "Enter text..."}: Props = $props()
let {
value = $bindable(),
addLabel,
placeholder = "Enter text...",
allowAdd = true,
}: Props & {allowAdd?: boolean} = $props()
let draggedIndex: number | null = $state(null)
const onChange = (newValue: string[]) => {
@@ -72,12 +77,14 @@
</div>
</div>
{/each}
<Button onclick={addItem} class="btn btn-link w-fit px-0">
<Icon icon={AddCircle} size={5} />
{#if addLabel}
{@render addLabel?.()}
{:else}
Add Item
{/if}
</Button>
{#if allowAdd}
<Button onclick={addItem} class="btn btn-link w-fit px-0">
<Icon icon={AddCircle} size={5} />
{#if addLabel}
{@render addLabel?.()}
{:else}
Add Item
{/if}
</Button>
{/if}
</div>
+1 -1
View File
@@ -13,7 +13,7 @@
const className = $derived(
cx(
props.class,
"scroll-container z-feature flex min-h-0 w-full min-w-0 flex-1 flex-col overflow-y-auto overflow-x-hidden",
"scroll-container z-feature flex min-h-0 w-full min-w-0 flex-col overflow-y-auto overflow-x-hidden",
),
)
</script>
+49 -24
View File
@@ -5,6 +5,7 @@
import {Badge} from "@capawesome/capacitor-badge"
import Bell from "@assets/icons/bell.svg?dataurl"
import {preventDefault} from "@lib/html"
import FieldInline from "@lib/components/FieldInline.svelte"
import Spinner from "@lib/components/Spinner.svelte"
import Icon from "@lib/components/Icon.svelte"
import Button from "@lib/components/Button.svelte"
@@ -63,40 +64,64 @@
<!-- pass -->
{:then { isSupported }}
{#if isSupported}
<div class="flex justify-between">
<p>Show badge for unread alerts</p>
<input type="checkbox" class="toggle toggle-primary" bind:checked={settings.badge} />
</div>
<FieldInline>
{#snippet label()}
<p>Show badge for unread alerts</p>
{/snippet}
{#snippet input()}
<input type="checkbox" class="toggle toggle-primary" bind:checked={settings.badge} />
{/snippet}
</FieldInline>
{/if}
{/await}
{#if !Capacitor.isNativePlatform()}
<div class="flex justify-between">
<p>Play sound for new activity</p>
<input type="checkbox" class="toggle toggle-primary" bind:checked={settings.sound} />
</div>
<FieldInline>
{#snippet label()}
<p>Play sound for new activity</p>
{/snippet}
{#snippet input()}
<input type="checkbox" class="toggle toggle-primary" bind:checked={settings.sound} />
{/snippet}
</FieldInline>
{/if}
<div class="flex justify-between">
<p>Enable push notifications</p>
<input type="checkbox" class="toggle toggle-primary" bind:checked={settings.push} />
</div>
<FieldInline>
{#snippet label()}
<p>Enable push notifications</p>
{/snippet}
{#snippet input()}
<input type="checkbox" class="toggle toggle-primary" bind:checked={settings.push} />
{/snippet}
</FieldInline>
</div>
<div
class={cx("card2 bg-alt col-4 shadow-md", {
"pointer-events-none opacity-50": !settings.badge && !settings.sound && !settings.push,
})}>
<strong class="text-lg">Alert Types</strong>
<div class="flex justify-between">
<p>Notify me about new activity</p>
<input type="checkbox" class="toggle toggle-primary" bind:checked={settings.spaces} />
</div>
<div class="flex justify-between">
<p>Always notify me when mentioned</p>
<input type="checkbox" class="toggle toggle-primary" checked={settings.mentions} />
</div>
<div class="flex justify-between">
<p>Notify me about new messages</p>
<input type="checkbox" class="toggle toggle-primary" bind:checked={settings.messages} />
</div>
<FieldInline>
{#snippet label()}
<p>Notify me about new activity</p>
{/snippet}
{#snippet input()}
<input type="checkbox" class="toggle toggle-primary" bind:checked={settings.spaces} />
{/snippet}
</FieldInline>
<FieldInline>
{#snippet label()}
<p>Always notify me when mentioned</p>
{/snippet}
{#snippet input()}
<input type="checkbox" class="toggle toggle-primary" checked={settings.mentions} />
{/snippet}
</FieldInline>
<FieldInline>
{#snippet label()}
<p>Notify me about new messages</p>
{/snippet}
{#snippet input()}
<input type="checkbox" class="toggle toggle-primary" bind:checked={settings.messages} />
{/snippet}
</FieldInline>
</div>
<div
class="card2 bg-alt sticky -bottom-3 shadow-md flex flex-row items-center justify-between gap-4">
+16 -9
View File
@@ -11,6 +11,7 @@
import {Router} from "@welshman/router"
import {userMuteList, tagPubkey, publishThunk, userBlossomServerList} from "@welshman/app"
import NotesMinimalistic from "@assets/icons/notes-minimalistic.svg?dataurl"
import AddCircle from "@assets/icons/add-circle.svg?dataurl"
import {preventDefault} from "@lib/html"
import Field from "@lib/components/Field.svelte"
import FieldInline from "@lib/components/FieldInline.svelte"
@@ -28,6 +29,10 @@
blossomServers = getTagValues("server", getListTags($userBlossomServerList))
}
const addServer = () => {
blossomServers = [...blossomServers, ""]
}
const onsubmit = preventDefault(async () => {
await publishSettings($state.snapshot(settings))
@@ -104,7 +109,7 @@
{/snippet}
{#snippet input()}
<input
class="range range-primary"
class="range range-primary w-full"
type="range"
min="0.8"
max="1.3"
@@ -115,13 +120,13 @@
</div>
<div class="card2 bg-alt col-4 shadow-md">
<strong class="text-lg">Editor Settings</strong>
<FieldInline>
<Field>
{#snippet label()}
<p>Send Delay</p>
{/snippet}
{#snippet input()}
<input
class="range range-primary"
class="range range-primary w-full"
type="range"
min="0"
max="10000"
@@ -134,17 +139,19 @@
{settings.send_delay === 1000 ? "second" : "seconds"}.
</p>
{/snippet}
</FieldInline>
</Field>
<Field>
{#snippet label()}
<p>Media Server</p>
{/snippet}
{#snippet secondary()}
<Button class="link text-sm underline flex items-center gap-1" onclick={addServer}>
<Icon icon={AddCircle} size={4} />
Add Server
</Button>
{/snippet}
{#snippet input()}
<InputList bind:value={blossomServers}>
{#snippet addLabel()}
Add Server
{/snippet}
</InputList>
<InputList allowAdd={false} bind:value={blossomServers} />
{/snippet}
{#snippet info()}
<p>Choose a media server type and url for files you upload to {PLATFORM_NAME}.</p>
+41 -25
View File
@@ -1,6 +1,7 @@
<script lang="ts">
import ShieldMinimalistic from "@assets/icons/shield-minimalistic.svg?dataurl"
import {preventDefault} from "@lib/html"
import FieldInline from "@lib/components/FieldInline.svelte"
import Icon from "@lib/components/Icon.svelte"
import Button from "@lib/components/Button.svelte"
import {pushToast} from "@app/util/toast"
@@ -30,31 +31,46 @@
<Icon icon={ShieldMinimalistic} />
Privacy Settings
</strong>
<div class="grid grid-cols-2 gap-2">
<p>Authenticate with unknown relays?</p>
<input
type="checkbox"
class="toggle toggle-primary"
onchange={onAuthModeChange}
checked={settings.auth_mode === RelayAuthMode.Aggressive} />
<p class="col-span-2 text-sm opacity-70">
Controls whether {PLATFORM_NAME} will identify you to relays not in your lists.
</p>
</div>
<div class="grid grid-cols-2 gap-2">
<p>Report errors?</p>
<input type="checkbox" class="toggle toggle-primary" bind:checked={settings.report_errors} />
<p class="col-span-2 text-sm opacity-70">
Allow {PLATFORM_NAME} to send error reports to help improve the app.
</p>
</div>
<div class="grid grid-cols-2 gap-2">
<p>Report usage?</p>
<input type="checkbox" class="toggle toggle-primary" bind:checked={settings.report_usage} />
<p class="col-span-2 text-sm opacity-70">
Allow {PLATFORM_NAME} to collect anonymous usage data.
</p>
</div>
<FieldInline>
{#snippet label()}
<p>Authenticate with unknown relays?</p>
{/snippet}
{#snippet input()}
<input
type="checkbox"
class="toggle toggle-primary"
onchange={onAuthModeChange}
checked={settings.auth_mode === RelayAuthMode.Aggressive} />
{/snippet}
{#snippet info()}
<p>Controls whether {PLATFORM_NAME} will identify you to relays not in your lists.</p>
{/snippet}
</FieldInline>
<FieldInline>
{#snippet label()}
<p>Report errors?</p>
{/snippet}
{#snippet input()}
<input
type="checkbox"
class="toggle toggle-primary"
bind:checked={settings.report_errors} />
{/snippet}
{#snippet info()}
<p>Allow {PLATFORM_NAME} to send error reports to help improve the app.</p>
{/snippet}
</FieldInline>
<FieldInline>
{#snippet label()}
<p>Report usage?</p>
{/snippet}
{#snippet input()}
<input type="checkbox" class="toggle toggle-primary" bind:checked={settings.report_usage} />
{/snippet}
{#snippet info()}
<p>Allow {PLATFORM_NAME} to collect anonymous usage data.</p>
{/snippet}
</FieldInline>
</div>
<div
class="card2 bg-alt sticky -bottom-3 shadow-md flex flex-row items-center justify-between gap-4">
+13 -1
View File
@@ -27,6 +27,7 @@
import PasswordReset from "@app/components/PasswordReset.svelte"
import InfoKeys from "@app/components/InfoKeys.svelte"
import {pushModal} from "@app/util/modal"
import {POMADE_NETWORK_ERROR_MESSAGE} from "@app/util/pomadeErrors"
import {clip, pushToast} from "@app/util/toast"
const npub = nip19.npubEncode($pubkey!)
@@ -48,13 +49,24 @@
const {ok, peersByPrefix} = await Client.requestChallenge($session!.email)
if (!ok) {
console.error("Pomade challenge request failed during password reset initiation")
pushToast({
theme: "error",
message: "Failed to initiate password reset!",
message: POMADE_NETWORK_ERROR_MESSAGE,
})
return
}
pushModal(PasswordReset, {peersByPrefix})
} catch (error) {
console.error(error)
pushToast({
theme: "error",
message: POMADE_NETWORK_ERROR_MESSAGE,
})
} finally {
loading = false
}
+9 -1
View File
@@ -4,6 +4,7 @@
import {page} from "$app/stores"
import {goto} from "$app/navigation"
import type {Readable} from "svelte/store"
import {debounce} from "throttle-debounce"
import {pubkey, publishThunk, waitForThunkError, joinRoom, leaveRoom} from "@welshman/app"
import {now, ifLet, int, formatTimestampAsDate, ago, MINUTE} from "@welshman/lib"
import type {MakeNonOptional} from "@welshman/lib"
@@ -244,6 +245,8 @@
const onScroll = () => {
if (!isProgrammaticScroll) {
userHasScrolled = true
isUserScrolling = true
clearIsUserScrolling()
manageScrollPosition()
}
@@ -265,6 +268,7 @@
let leaving = $state(false)
let userHasScrolled = $state(false)
let isProgrammaticScroll = $state(false)
let isUserScrolling = $state(false)
let loadingBackward = $state(true)
let loadingForward = $state(true)
let share = $state(popKey<TrustedEvent | undefined>("share"))
@@ -278,6 +282,10 @@
let compose: RoomCompose | undefined = $state()
let eventToEdit: TrustedEvent | undefined = $state()
const clearIsUserScrolling = debounce(150, () => {
isUserScrolling = false
})
const elements = $derived.by(() => {
const elements = []
const seen = new Set()
@@ -351,7 +359,7 @@
})
$effect(() => {
if (elements.length > 0) {
if (elements.length > 0 && !isUserScrolling) {
requestAnimationFrame(manageScrollPosition)
}
})
+9 -1
View File
@@ -4,6 +4,7 @@
import {goto} from "$app/navigation"
import type {Readable} from "svelte/store"
import {readable} from "svelte/store"
import {debounce} from "throttle-debounce"
import {now, int, ifLet, formatTimestampAsDate, MINUTE, ago} from "@welshman/lib"
import type {TrustedEvent, EventContent} from "@welshman/util"
import {makeEvent, MESSAGE, RELAY_ADD_MEMBER} from "@welshman/util"
@@ -139,6 +140,8 @@
const onScroll = () => {
if (!isProgrammaticScroll) {
userHasScrolled = true
isUserScrolling = true
clearIsUserScrolling()
manageScrollPosition()
}
@@ -160,6 +163,7 @@
let loadingForward = $state(true)
let userHasScrolled = $state(false)
let isProgrammaticScroll = $state(false)
let isUserScrolling = $state(false)
let share = $state(popKey<TrustedEvent | undefined>("share"))
let parent: TrustedEvent | undefined = $state()
let element: HTMLElement | undefined = $state()
@@ -171,6 +175,10 @@
let compose: RoomCompose | undefined = $state()
let eventToEdit: TrustedEvent | undefined = $state()
const clearIsUserScrolling = debounce(150, () => {
isUserScrolling = false
})
const elements = $derived.by(() => {
const elements = []
const seen = new Set()
@@ -244,7 +252,7 @@
})
$effect(() => {
if (elements.length > 0) {
if (elements.length > 0 && !isUserScrolling) {
requestAnimationFrame(manageScrollPosition)
}
})