Merge branch 'dev' of https://gitea.coracle.social/DeveshSingh/flotilla into feat/signup-progress-bar

This commit is contained in:
deveshanim3
2026-04-18 05:26:34 +05:30
12 changed files with 206 additions and 88 deletions
+10 -2
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
} }
+11 -1
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
} }
+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
} }
+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
+17 -11
View File
@@ -9,16 +9,22 @@
const {...props}: Props = $props() const {...props}: Props = $props()
</script> </script>
<div class="grid grid-cols-1 gap-2 lg:gap-6 lg:grid-cols-3 {props.class}"> <div class="flex flex-col gap-2 {props.class}">
<label class="flex items-center gap-2 font-bold"> <div class="flex items-center justify-between w-full gap-2">
{@render props.label?.()} {#if props.label}
</label> <label class="flex items-center gap-2 max-w-[80%] md:max-w-none">
<div class="col-span-2 flex items-center gap-2"> {@render props.label()}
{@render props.input?.()} </label>
</div>
<p class="flex-end text-sm opacity-50 lg:col-span-3">
{#if props.info}
{@render props.info?.()}
{/if} {/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> </div>
+16 -9
View File
@@ -13,7 +13,12 @@
placeholder?: string 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) let draggedIndex: number | null = $state(null)
const onChange = (newValue: string[]) => { const onChange = (newValue: string[]) => {
@@ -72,12 +77,14 @@
</div> </div>
</div> </div>
{/each} {/each}
<Button onclick={addItem} class="btn btn-link w-fit px-0"> {#if allowAdd}
<Icon icon={AddCircle} size={5} /> <Button onclick={addItem} class="btn btn-link w-fit px-0">
{#if addLabel} <Icon icon={AddCircle} size={5} />
{@render addLabel?.()} {#if addLabel}
{:else} {@render addLabel?.()}
Add Item {:else}
{/if} Add Item
</Button> {/if}
</Button>
{/if}
</div> </div>
+1 -1
View File
@@ -13,7 +13,7 @@
const className = $derived( const className = $derived(
cx( cx(
props.class, 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> </script>
+49 -24
View File
@@ -5,6 +5,7 @@
import {Badge} from "@capawesome/capacitor-badge" import {Badge} from "@capawesome/capacitor-badge"
import Bell from "@assets/icons/bell.svg?dataurl" import Bell from "@assets/icons/bell.svg?dataurl"
import {preventDefault} from "@lib/html" import {preventDefault} from "@lib/html"
import FieldInline from "@lib/components/FieldInline.svelte"
import Spinner from "@lib/components/Spinner.svelte" import Spinner from "@lib/components/Spinner.svelte"
import Icon from "@lib/components/Icon.svelte" import Icon from "@lib/components/Icon.svelte"
import Button from "@lib/components/Button.svelte" import Button from "@lib/components/Button.svelte"
@@ -63,40 +64,64 @@
<!-- pass --> <!-- pass -->
{:then { isSupported }} {:then { isSupported }}
{#if isSupported} {#if isSupported}
<div class="flex justify-between"> <FieldInline>
<p>Show badge for unread alerts</p> {#snippet label()}
<input type="checkbox" class="toggle toggle-primary" bind:checked={settings.badge} /> <p>Show badge for unread alerts</p>
</div> {/snippet}
{#snippet input()}
<input type="checkbox" class="toggle toggle-primary" bind:checked={settings.badge} />
{/snippet}
</FieldInline>
{/if} {/if}
{/await} {/await}
{#if !Capacitor.isNativePlatform()} {#if !Capacitor.isNativePlatform()}
<div class="flex justify-between"> <FieldInline>
<p>Play sound for new activity</p> {#snippet label()}
<input type="checkbox" class="toggle toggle-primary" bind:checked={settings.sound} /> <p>Play sound for new activity</p>
</div> {/snippet}
{#snippet input()}
<input type="checkbox" class="toggle toggle-primary" bind:checked={settings.sound} />
{/snippet}
</FieldInline>
{/if} {/if}
<div class="flex justify-between"> <FieldInline>
<p>Enable push notifications</p> {#snippet label()}
<input type="checkbox" class="toggle toggle-primary" bind:checked={settings.push} /> <p>Enable push notifications</p>
</div> {/snippet}
{#snippet input()}
<input type="checkbox" class="toggle toggle-primary" bind:checked={settings.push} />
{/snippet}
</FieldInline>
</div> </div>
<div <div
class={cx("card2 bg-alt col-4 shadow-md", { class={cx("card2 bg-alt col-4 shadow-md", {
"pointer-events-none opacity-50": !settings.badge && !settings.sound && !settings.push, "pointer-events-none opacity-50": !settings.badge && !settings.sound && !settings.push,
})}> })}>
<strong class="text-lg">Alert Types</strong> <strong class="text-lg">Alert Types</strong>
<div class="flex justify-between"> <FieldInline>
<p>Notify me about new activity</p> {#snippet label()}
<input type="checkbox" class="toggle toggle-primary" bind:checked={settings.spaces} /> <p>Notify me about new activity</p>
</div> {/snippet}
<div class="flex justify-between"> {#snippet input()}
<p>Always notify me when mentioned</p> <input type="checkbox" class="toggle toggle-primary" bind:checked={settings.spaces} />
<input type="checkbox" class="toggle toggle-primary" checked={settings.mentions} /> {/snippet}
</div> </FieldInline>
<div class="flex justify-between"> <FieldInline>
<p>Notify me about new messages</p> {#snippet label()}
<input type="checkbox" class="toggle toggle-primary" bind:checked={settings.messages} /> <p>Always notify me when mentioned</p>
</div> {/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>
<div <div
class="card2 bg-alt sticky -bottom-3 shadow-md flex flex-row items-center justify-between gap-4"> 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 {Router} from "@welshman/router"
import {userMuteList, tagPubkey, publishThunk, userBlossomServerList} from "@welshman/app" import {userMuteList, tagPubkey, publishThunk, userBlossomServerList} from "@welshman/app"
import NotesMinimalistic from "@assets/icons/notes-minimalistic.svg?dataurl" import NotesMinimalistic from "@assets/icons/notes-minimalistic.svg?dataurl"
import AddCircle from "@assets/icons/add-circle.svg?dataurl"
import {preventDefault} from "@lib/html" import {preventDefault} from "@lib/html"
import Field from "@lib/components/Field.svelte" import Field from "@lib/components/Field.svelte"
import FieldInline from "@lib/components/FieldInline.svelte" import FieldInline from "@lib/components/FieldInline.svelte"
@@ -28,6 +29,10 @@
blossomServers = getTagValues("server", getListTags($userBlossomServerList)) blossomServers = getTagValues("server", getListTags($userBlossomServerList))
} }
const addServer = () => {
blossomServers = [...blossomServers, ""]
}
const onsubmit = preventDefault(async () => { const onsubmit = preventDefault(async () => {
await publishSettings($state.snapshot(settings)) await publishSettings($state.snapshot(settings))
@@ -104,7 +109,7 @@
{/snippet} {/snippet}
{#snippet input()} {#snippet input()}
<input <input
class="range range-primary" class="range range-primary w-full"
type="range" type="range"
min="0.8" min="0.8"
max="1.3" max="1.3"
@@ -115,13 +120,13 @@
</div> </div>
<div class="card2 bg-alt col-4 shadow-md"> <div class="card2 bg-alt col-4 shadow-md">
<strong class="text-lg">Editor Settings</strong> <strong class="text-lg">Editor Settings</strong>
<FieldInline> <Field>
{#snippet label()} {#snippet label()}
<p>Send Delay</p> <p>Send Delay</p>
{/snippet} {/snippet}
{#snippet input()} {#snippet input()}
<input <input
class="range range-primary" class="range range-primary w-full"
type="range" type="range"
min="0" min="0"
max="10000" max="10000"
@@ -134,17 +139,19 @@
{settings.send_delay === 1000 ? "second" : "seconds"}. {settings.send_delay === 1000 ? "second" : "seconds"}.
</p> </p>
{/snippet} {/snippet}
</FieldInline> </Field>
<Field> <Field>
{#snippet label()} {#snippet label()}
<p>Media Server</p> <p>Media Server</p>
{/snippet} {/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()} {#snippet input()}
<InputList bind:value={blossomServers}> <InputList allowAdd={false} bind:value={blossomServers} />
{#snippet addLabel()}
Add Server
{/snippet}
</InputList>
{/snippet} {/snippet}
{#snippet info()} {#snippet info()}
<p>Choose a media server type and url for files you upload to {PLATFORM_NAME}.</p> <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"> <script lang="ts">
import ShieldMinimalistic from "@assets/icons/shield-minimalistic.svg?dataurl" import ShieldMinimalistic from "@assets/icons/shield-minimalistic.svg?dataurl"
import {preventDefault} from "@lib/html" import {preventDefault} from "@lib/html"
import FieldInline from "@lib/components/FieldInline.svelte"
import Icon from "@lib/components/Icon.svelte" import Icon from "@lib/components/Icon.svelte"
import Button from "@lib/components/Button.svelte" import Button from "@lib/components/Button.svelte"
import {pushToast} from "@app/util/toast" import {pushToast} from "@app/util/toast"
@@ -30,31 +31,46 @@
<Icon icon={ShieldMinimalistic} /> <Icon icon={ShieldMinimalistic} />
Privacy Settings Privacy Settings
</strong> </strong>
<div class="grid grid-cols-2 gap-2"> <FieldInline>
<p>Authenticate with unknown relays?</p> {#snippet label()}
<input <p>Authenticate with unknown relays?</p>
type="checkbox" {/snippet}
class="toggle toggle-primary" {#snippet input()}
onchange={onAuthModeChange} <input
checked={settings.auth_mode === RelayAuthMode.Aggressive} /> type="checkbox"
<p class="col-span-2 text-sm opacity-70"> class="toggle toggle-primary"
Controls whether {PLATFORM_NAME} will identify you to relays not in your lists. onchange={onAuthModeChange}
</p> checked={settings.auth_mode === RelayAuthMode.Aggressive} />
</div> {/snippet}
<div class="grid grid-cols-2 gap-2"> {#snippet info()}
<p>Report errors?</p> <p>Controls whether {PLATFORM_NAME} will identify you to relays not in your lists.</p>
<input type="checkbox" class="toggle toggle-primary" bind:checked={settings.report_errors} /> {/snippet}
<p class="col-span-2 text-sm opacity-70"> </FieldInline>
Allow {PLATFORM_NAME} to send error reports to help improve the app. <FieldInline>
</p> {#snippet label()}
</div> <p>Report errors?</p>
<div class="grid grid-cols-2 gap-2"> {/snippet}
<p>Report usage?</p> {#snippet input()}
<input type="checkbox" class="toggle toggle-primary" bind:checked={settings.report_usage} /> <input
<p class="col-span-2 text-sm opacity-70"> type="checkbox"
Allow {PLATFORM_NAME} to collect anonymous usage data. class="toggle toggle-primary"
</p> bind:checked={settings.report_errors} />
</div> {/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>
<div <div
class="card2 bg-alt sticky -bottom-3 shadow-md flex flex-row items-center justify-between gap-4"> 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 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
} }