Compare commits

..

8 Commits

Author SHA1 Message Date
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
16 changed files with 75 additions and 30 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>
+1 -1
View File
@@ -90,7 +90,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>
+1 -1
View File
@@ -61,7 +61,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>
+1 -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>
+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}
+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}>
+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"
] ]
} }
} }