Compare commits

..

10 Commits

Author SHA1 Message Date
userAdityaa d0b8b581cd chore: carify Pomade login errors with actionable invalid vs network messaging 2026-04-18 04:14:25 +05:45
deveshanim3 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
deveshanim3 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
deveshanim3 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
Jon Staab 95d9d8bf23 Bump version 2026-04-16 14:10:50 -07:00
Jon Staab 2fd9741a2b Fix safe area inset for chat fab
Docker / build-and-push-image (push) Successful in 16m42s
2026-04-16 14:08:25 -07:00
Jon Staab fe9c325580 Update universal links 2026-04-16 13:50:13 -07:00
20 changed files with 144 additions and 39 deletions
+4
View File
@@ -1,5 +1,9 @@
# Changelog # Changelog
# 1.7.4
* Fix safe area inset for FAB
# 1.7.3 # 1.7.3
* Add native share support for space invites * Add native share support for space invites
+2 -2
View File
@@ -8,8 +8,8 @@ android {
applicationId "social.flotilla" applicationId "social.flotilla"
minSdk rootProject.ext.minSdkVersion minSdk rootProject.ext.minSdkVersion
targetSdk rootProject.ext.targetSdkVersion targetSdk rootProject.ext.targetSdkVersion
versionCode 45 versionCode 46
versionName "1.7.3" versionName "1.7.4"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
aaptOptions { aaptOptions {
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps. // Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
+4 -4
View File
@@ -358,14 +358,14 @@
CODE_SIGN_ENTITLEMENTS = "Flotilla Chat.entitlements"; CODE_SIGN_ENTITLEMENTS = "Flotilla Chat.entitlements";
CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 36; CURRENT_PROJECT_VERSION = 37;
DEVELOPMENT_TEAM = S26U9DYW3A; DEVELOPMENT_TEAM = S26U9DYW3A;
INFOPLIST_FILE = App/Info.plist; INFOPLIST_FILE = App/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = "Flotilla Chat"; INFOPLIST_KEY_CFBundleDisplayName = "Flotilla Chat";
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.social-networking"; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.social-networking";
IPHONEOS_DEPLOYMENT_TARGET = 15.0; IPHONEOS_DEPLOYMENT_TARGET = 15.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
MARKETING_VERSION = 1.7.3; MARKETING_VERSION = 1.7.4;
OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\""; OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\"";
PRODUCT_BUNDLE_IDENTIFIER = social.flotilla; PRODUCT_BUNDLE_IDENTIFIER = social.flotilla;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
@@ -385,14 +385,14 @@
CODE_SIGN_ENTITLEMENTS = "Flotilla Chat.entitlements"; CODE_SIGN_ENTITLEMENTS = "Flotilla Chat.entitlements";
CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 36; CURRENT_PROJECT_VERSION = 37;
DEVELOPMENT_TEAM = S26U9DYW3A; DEVELOPMENT_TEAM = S26U9DYW3A;
INFOPLIST_FILE = App/Info.plist; INFOPLIST_FILE = App/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = "Flotilla Chat"; INFOPLIST_KEY_CFBundleDisplayName = "Flotilla Chat";
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.social-networking"; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.social-networking";
IPHONEOS_DEPLOYMENT_TARGET = 15.0; IPHONEOS_DEPLOYMENT_TARGET = 15.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
MARKETING_VERSION = 1.7.3; MARKETING_VERSION = 1.7.4;
PRODUCT_BUNDLE_IDENTIFIER = social.flotilla; PRODUCT_BUNDLE_IDENTIFIER = social.flotilla;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "flotilla", "name": "flotilla",
"version": "1.7.3", "version": "1.7.4",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "vite dev", "dev": "vite dev",
+10 -10
View File
@@ -2,6 +2,16 @@
@config "../tailwind.config.js"; @config "../tailwind.config.js";
/* root */
:root {
font-family: Lato;
--sait: var(--safe-area-inset-top, env(safe-area-inset-top));
--saib: var(--safe-area-inset-bottom, env(safe-area-inset-bottom));
--sail: var(--safe-area-inset-left, env(safe-area-inset-left));
--sair: var(--safe-area-inset-right, env(safe-area-inset-right));
}
@utility pt-sai { @utility pt-sai {
padding-top: var(--sait); padding-top: var(--sait);
} }
@@ -22,16 +32,6 @@
@apply pl-sai pr-sai; @apply pl-sai pr-sai;
} }
/* root */
:root {
font-family: Lato;
--sait: var(--safe-area-inset-top, env(safe-area-inset-top));
--saib: var(--safe-area-inset-bottom, env(safe-area-inset-bottom));
--sail: var(--safe-area-inset-left, env(safe-area-inset-left));
--sair: var(--safe-area-inset-right, env(safe-area-inset-right));
}
@utility py-sai { @utility py-sai {
@apply pt-sai pb-sai; @apply pt-sai pb-sai;
} }
+3 -2
View File
@@ -42,7 +42,7 @@
let popover: Instance | undefined = $state() let popover: Instance | undefined = $state()
</script> </script>
<Button class="join rounded-full"> <div class="join items-center rounded-full">
{#if ENABLE_ZAPS && !hideZap} {#if ENABLE_ZAPS && !hideZap}
<ZapButton {url} {event} class="btn join-item btn-neutral btn-xs"> <ZapButton {url} {event} class="btn join-item btn-neutral btn-xs">
<Icon icon={Bolt} size={4} /> <Icon icon={Bolt} size={4} />
@@ -52,6 +52,7 @@
<Icon icon={SmileCircle} size={4} /> <Icon icon={SmileCircle} size={4} />
</EmojiButton> </EmojiButton>
<Tippy <Tippy
class="flex"
bind:popover bind:popover
component={EventMenu} component={EventMenu}
props={{url, noun, event, customActions, onClick: hidePopover}} props={{url, noun, event, customActions, onClick: hidePopover}}
@@ -60,4 +61,4 @@
<Icon icon={MenuDots} size={4} /> <Icon icon={MenuDots} size={4} />
</Button> </Button>
</Tippy> </Tippy>
</Button> </div>
+11 -3
View File
@@ -19,6 +19,7 @@
import LogInOTP from "@app/components/LogInOTP.svelte" import LogInOTP from "@app/components/LogInOTP.svelte"
import LogInSelect from "@app/components/LogInSelect.svelte" import LogInSelect from "@app/components/LogInSelect.svelte"
import {deleteDeactivatedPomadeSessions, loginWithPomade} from "@app/util/pomade" 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 {pushModal, clearModals} from "@app/util/modal"
import {setChecked} from "@app/util/notifications" import {setChecked} from "@app/util/notifications"
import {pushToast} from "@app/util/toast" import {pushToast} from "@app/util/toast"
@@ -44,7 +45,7 @@
return pushToast({ return pushToast({
theme: "error", theme: "error",
message: "Sorry, we were unable to log you in.", message: getPomadeLoginFailureMessage(messages),
}) })
} }
@@ -64,10 +65,17 @@
pushToast({ pushToast({
theme: "error", 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 { } finally {
loading = false loading = false
} }
@@ -90,7 +98,7 @@
{#snippet input()} {#snippet input()}
<label class="input input-bordered flex w-full items-center gap-2"> <label class="input input-bordered flex w-full items-center gap-2">
<Icon icon={Letter} /> <Icon icon={Letter} />
<input bind:value={email} /> <input type="email" bind:value={email} />
</label> </label>
{/snippet} {/snippet}
</FieldInline> </FieldInline>
+12 -2
View File
@@ -15,6 +15,7 @@
import ModalSubtitle from "@lib/components/ModalSubtitle.svelte" import ModalSubtitle from "@lib/components/ModalSubtitle.svelte"
import ModalFooter from "@lib/components/ModalFooter.svelte" import ModalFooter from "@lib/components/ModalFooter.svelte"
import LogInOTPConfirm from "@app/components/LogInOTPConfirm.svelte" import LogInOTPConfirm from "@app/components/LogInOTPConfirm.svelte"
import {POMADE_NETWORK_ERROR_MESSAGE} from "@app/util/pomadeErrors"
import {pushModal} from "@app/util/modal" import {pushModal} from "@app/util/modal"
import {pushToast} from "@app/util/toast" import {pushToast} from "@app/util/toast"
@@ -35,11 +36,20 @@
if (ok) { if (ok) {
pushModal(LogInOTPConfirm, {email, peersByPrefix}) pushModal(LogInOTPConfirm, {email, peersByPrefix})
} else { } else {
console.error("Pomade challenge request failed during OTP login")
pushToast({ pushToast({
theme: "error", 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 { } finally {
loading = false loading = false
} }
@@ -61,7 +71,7 @@
{#snippet input()} {#snippet input()}
<label class="input input-bordered flex w-full items-center gap-2"> <label class="input input-bordered flex w-full items-center gap-2">
<Icon icon={Letter} /> <Icon icon={Letter} />
<input bind:value={email} /> <input type="email" bind:value={email} />
</label> </label>
{/snippet} {/snippet}
</FieldInline> </FieldInline>
+12 -4
View File
@@ -15,10 +15,11 @@
import ModalFooter from "@lib/components/ModalFooter.svelte" import ModalFooter from "@lib/components/ModalFooter.svelte"
import StringMultiInput from "@lib/components/StringMultiInput.svelte" import StringMultiInput from "@lib/components/StringMultiInput.svelte"
import LogInSelect from "@app/components/LogInSelect.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 {pushModal, clearModals} from "@app/util/modal"
import {setChecked} from "@app/util/notifications"
import {deleteDeactivatedPomadeSessions, loginWithPomade} from "@app/util/pomade" 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 = { type Props = {
email: string email: string
@@ -44,7 +45,7 @@
return pushToast({ return pushToast({
theme: "error", theme: "error",
message: "Sorry, we were unable to log you in.", message: getPomadeLoginFailureMessage(messages),
}) })
} }
@@ -64,10 +65,17 @@
pushToast({ pushToast({
theme: "error", 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 { } finally {
loading = false loading = false
} }
+9 -1
View File
@@ -14,6 +14,7 @@
import ModalFooter from "@lib/components/ModalFooter.svelte" import ModalFooter from "@lib/components/ModalFooter.svelte"
import Profile from "@app/components/Profile.svelte" import Profile from "@app/components/Profile.svelte"
import {deleteDeactivatedPomadeSessions, loginWithPomade} from "@app/util/pomade" import {deleteDeactivatedPomadeSessions, loginWithPomade} from "@app/util/pomade"
import {getPomadeLoginFailureMessage, POMADE_NETWORK_ERROR_MESSAGE} from "@app/util/pomadeErrors"
import {setChecked} from "@app/util/notifications" import {setChecked} from "@app/util/notifications"
import {clearModals} from "@app/util/modal" import {clearModals} from "@app/util/modal"
import {pushToast} from "@app/util/toast" import {pushToast} from "@app/util/toast"
@@ -46,9 +47,16 @@
pushToast({ pushToast({
theme: "error", 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 { } finally {
loading = false loading = false
} }
+4 -1
View File
@@ -120,7 +120,7 @@
{#snippet input()} {#snippet input()}
<label class="input input-bordered flex w-full items-center gap-2"> <label class="input input-bordered flex w-full items-center gap-2">
<Icon icon={Letter} /> <Icon icon={Letter} />
<input bind:value={email} /> <input type="email" bind:value={email} />
</label> </label>
{/snippet} {/snippet}
</FieldInline> </FieldInline>
@@ -134,6 +134,9 @@
<input type="password" bind:value={password} /> <input type="password" bind:value={password} />
</label> </label>
{/snippet} {/snippet}
{#snippet info()}
Must be at least 12 characters long.
{/snippet}
</FieldInline> </FieldInline>
</ModalBody> </ModalBody>
<ModalFooter> <ModalFooter>
+22 -2
View File
@@ -25,6 +25,16 @@
const {url} = $props() const {url} = $props()
const authError = deriveRelayAuthError(url) 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 back = () => history.back()
const copyInvite = () => clip(invite) const copyInvite = () => clip(invite)
@@ -70,8 +80,14 @@
]) ])
claim = getTagValue("claim", event?.tags || []) || "" claim = getTagValue("claim", event?.tags || []) || ""
} catch { } catch (err) {
claim = "" claim = ""
if (
(err instanceof Error && (err.name === "AbortError" || err.name === "TimeoutError")) ||
!navigator.onLine
) {
networkError = true
}
} finally { } finally {
loading = false loading = false
} }
@@ -92,7 +108,11 @@
<p class="center"> <p class="center">
<Spinner {loading}>Requesting an invite link...</Spinner> <Spinner {loading}>Requesting an invite link...</Spinner>
</p> </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> <p class="center">Oops! It looks like you're not a member of this relay.</p>
{:else} {:else}
<div class="flex flex-col items-center gap-6"> <div class="flex flex-col items-center gap-6">
+1 -1
View File
@@ -46,7 +46,7 @@
</div> </div>
{/if} {/if}
{#if Array.isArray(supported_nips)} {#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> <span class="ellipsize">Supported NIPs: {supported_nips.join(", ")}</span>
</p> </p>
{/if} {/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} /> <svelte:document onmousemove={onMouseMove} />
<Tippy <Tippy
class="flex"
bind:popover bind:popover
component={EmojiPicker} component={EmojiPicker}
props={{onClick}} props={{onClick}}
+1 -1
View File
@@ -14,7 +14,7 @@
} = $props() } = $props()
</script> </script>
<div class={cx("fixed bottom-20 right-4 z-nav hide-on-keyboard md:hidden", className)}> <div class={cx("fixed bottom-20 mb-sai right-4 z-nav hide-on-keyboard md:hidden", className)}>
<Button <Button
class="btn btn-primary border-none shadow-xl hover:opacity-90 transition-all size-[50px] rounded-xl p-0" class="btn btn-primary border-none shadow-xl hover:opacity-90 transition-all size-[50px] rounded-xl p-0"
{onclick}> {onclick}>
+13 -1
View File
@@ -27,6 +27,7 @@
import PasswordReset from "@app/components/PasswordReset.svelte" import PasswordReset from "@app/components/PasswordReset.svelte"
import InfoKeys from "@app/components/InfoKeys.svelte" import InfoKeys from "@app/components/InfoKeys.svelte"
import {pushModal} from "@app/util/modal" import {pushModal} from "@app/util/modal"
import {POMADE_NETWORK_ERROR_MESSAGE} from "@app/util/pomadeErrors"
import {clip, pushToast} from "@app/util/toast" import {clip, pushToast} from "@app/util/toast"
const npub = nip19.npubEncode($pubkey!) const npub = nip19.npubEncode($pubkey!)
@@ -48,13 +49,24 @@
const {ok, peersByPrefix} = await Client.requestChallenge($session!.email) const {ok, peersByPrefix} = await Client.requestChallenge($session!.email)
if (!ok) { if (!ok) {
console.error("Pomade challenge request failed during password reset initiation")
pushToast({ pushToast({
theme: "error", theme: "error",
message: "Failed to initiate password reset!", message: POMADE_NETWORK_ERROR_MESSAGE,
}) })
return
} }
pushModal(PasswordReset, {peersByPrefix}) pushModal(PasswordReset, {peersByPrefix})
} catch (error) {
console.error(error)
pushToast({
theme: "error",
message: POMADE_NETWORK_ERROR_MESSAGE,
})
} finally { } finally {
loading = false loading = false
} }
+9 -1
View File
@@ -4,6 +4,7 @@
import {page} from "$app/stores" import {page} from "$app/stores"
import {goto} from "$app/navigation" import {goto} from "$app/navigation"
import type {Readable} from "svelte/store" import type {Readable} from "svelte/store"
import {debounce} from "throttle-debounce"
import {pubkey, publishThunk, waitForThunkError, joinRoom, leaveRoom} from "@welshman/app" import {pubkey, publishThunk, waitForThunkError, joinRoom, leaveRoom} from "@welshman/app"
import {now, ifLet, int, formatTimestampAsDate, ago, MINUTE} from "@welshman/lib" import {now, ifLet, int, formatTimestampAsDate, ago, MINUTE} from "@welshman/lib"
import type {MakeNonOptional} from "@welshman/lib" import type {MakeNonOptional} from "@welshman/lib"
@@ -244,6 +245,8 @@
const onScroll = () => { const onScroll = () => {
if (!isProgrammaticScroll) { if (!isProgrammaticScroll) {
userHasScrolled = true userHasScrolled = true
isUserScrolling = true
clearIsUserScrolling()
manageScrollPosition() manageScrollPosition()
} }
@@ -265,6 +268,7 @@
let leaving = $state(false) let leaving = $state(false)
let userHasScrolled = $state(false) let userHasScrolled = $state(false)
let isProgrammaticScroll = $state(false) let isProgrammaticScroll = $state(false)
let isUserScrolling = $state(false)
let loadingBackward = $state(true) let loadingBackward = $state(true)
let loadingForward = $state(true) let loadingForward = $state(true)
let share = $state(popKey<TrustedEvent | undefined>("share")) let share = $state(popKey<TrustedEvent | undefined>("share"))
@@ -278,6 +282,10 @@
let compose: RoomCompose | undefined = $state() let compose: RoomCompose | undefined = $state()
let eventToEdit: TrustedEvent | undefined = $state() let eventToEdit: TrustedEvent | undefined = $state()
const clearIsUserScrolling = debounce(150, () => {
isUserScrolling = false
})
const elements = $derived.by(() => { const elements = $derived.by(() => {
const elements = [] const elements = []
const seen = new Set() const seen = new Set()
@@ -351,7 +359,7 @@
}) })
$effect(() => { $effect(() => {
if (elements.length > 0) { if (elements.length > 0 && !isUserScrolling) {
requestAnimationFrame(manageScrollPosition) requestAnimationFrame(manageScrollPosition)
} }
}) })
+9 -1
View File
@@ -4,6 +4,7 @@
import {goto} from "$app/navigation" import {goto} from "$app/navigation"
import type {Readable} from "svelte/store" import type {Readable} from "svelte/store"
import {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 {now, int, ifLet, formatTimestampAsDate, MINUTE, ago} from "@welshman/lib"
import type {TrustedEvent, EventContent} from "@welshman/util" import type {TrustedEvent, EventContent} from "@welshman/util"
import {makeEvent, MESSAGE, RELAY_ADD_MEMBER} from "@welshman/util" import {makeEvent, MESSAGE, RELAY_ADD_MEMBER} from "@welshman/util"
@@ -139,6 +140,8 @@
const onScroll = () => { const onScroll = () => {
if (!isProgrammaticScroll) { if (!isProgrammaticScroll) {
userHasScrolled = true userHasScrolled = true
isUserScrolling = true
clearIsUserScrolling()
manageScrollPosition() manageScrollPosition()
} }
@@ -160,6 +163,7 @@
let loadingForward = $state(true) let loadingForward = $state(true)
let userHasScrolled = $state(false) let userHasScrolled = $state(false)
let isProgrammaticScroll = $state(false) let isProgrammaticScroll = $state(false)
let isUserScrolling = $state(false)
let share = $state(popKey<TrustedEvent | undefined>("share")) let share = $state(popKey<TrustedEvent | undefined>("share"))
let parent: TrustedEvent | undefined = $state() let parent: TrustedEvent | undefined = $state()
let element: HTMLElement | undefined = $state() let element: HTMLElement | undefined = $state()
@@ -171,6 +175,10 @@
let compose: RoomCompose | undefined = $state() let compose: RoomCompose | undefined = $state()
let eventToEdit: TrustedEvent | undefined = $state() let eventToEdit: TrustedEvent | undefined = $state()
const clearIsUserScrolling = debounce(150, () => {
isUserScrolling = false
})
const elements = $derived.by(() => { const elements = $derived.by(() => {
const elements = [] const elements = []
const seen = new Set() const seen = new Set()
@@ -244,7 +252,7 @@
}) })
$effect(() => { $effect(() => {
if (elements.length > 0) { if (elements.length > 0 && !isUserScrolling) {
requestAnimationFrame(manageScrollPosition) requestAnimationFrame(manageScrollPosition)
} }
}) })
+5 -2
View File
@@ -1,12 +1,15 @@
[ [
{ {
"relation": ["delegate_permission/common.handle_all_urls"], "relation": [
"delegate_permission/common.handle_all_urls"
],
"target": { "target": {
"namespace": "android_app", "namespace": "android_app",
"package_name": "social.flotilla", "package_name": "social.flotilla",
"sha256_cert_fingerprints": [ "sha256_cert_fingerprints": [
"D0:2A:2E:82:75:92:4D:E2:13:E8:46:B8:EA:09:15:17:7F:46:7B:D1:49:E3:12:60:F0:01:D3:EF:42:9B:A2:DA", "D0:2A:2E:82:75:92:4D:E2:13:E8:46:B8:EA:09:15:17:7F:46:7B:D1:49:E3:12:60:F0:01:D3:EF:42:9B:A2:DA",
"6D:AF:68:3E:1C:A8:3A:4C:D8:85:73:E9:73:9E:2A:A9:44:C8:5D:56:15:4E:34:42:30:55:7C:FF:ED:4A:D7:8C" "6D:AF:68:3E:1C:A8:3A:4C:D8:85:73:E9:73:9E:2A:A9:44:C8:5D:56:15:4E:34:42:30:55:7C:FF:ED:4A:D7:8C",
"8C:EE:37:F9:8A:08:02:A7:BB:55:2B:64:E5:A5:93:D8:58:73:14:26:66:71:DD:B0:4F:AB:9D:D5:4C:DF:FB:F7"
] ]
} }
} }