forked from coracle/flotilla
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 53954aae89 | |||
| 24aa62a503 | |||
| 2618bb9c63 | |||
| 32a31045ef | |||
| 56edad77a8 | |||
| fdb604e350 | |||
| 3c66dfd83c | |||
| 81633b0a1e | |||
| 4a967de184 | |||
| 59961cbdb5 |
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||
@@ -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}
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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}}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user