forked from coracle/flotilla
Allow users to opt-in to spaces that strip signatures
This commit is contained in:
@@ -49,7 +49,7 @@
|
|||||||
import ThunkToast from "@app/components/ThunkToast.svelte"
|
import ThunkToast from "@app/components/ThunkToast.svelte"
|
||||||
import {
|
import {
|
||||||
INDEXER_RELAYS,
|
INDEXER_RELAYS,
|
||||||
userSettingValues,
|
userSettingsValues,
|
||||||
deriveChat,
|
deriveChat,
|
||||||
splitChatId,
|
splitChatId,
|
||||||
PLATFORM_NAME,
|
PLATFORM_NAME,
|
||||||
@@ -130,7 +130,7 @@
|
|||||||
const template = templates[i]
|
const template = templates[i]
|
||||||
|
|
||||||
thunks.push(
|
thunks.push(
|
||||||
await sendWrapped({pubkeys, template, delay: $userSettingValues.send_delay + ms(i)}),
|
await sendWrapped({pubkeys, template, delay: $userSettingsValues.send_delay + ms(i)}),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,7 +31,7 @@
|
|||||||
import ContentQuote from "@app/components/ContentQuote.svelte"
|
import ContentQuote from "@app/components/ContentQuote.svelte"
|
||||||
import ContentTopic from "@app/components/ContentTopic.svelte"
|
import ContentTopic from "@app/components/ContentTopic.svelte"
|
||||||
import ContentMention from "@app/components/ContentMention.svelte"
|
import ContentMention from "@app/components/ContentMention.svelte"
|
||||||
import {entityLink, userSettingValues} from "@app/core/state"
|
import {entityLink, userSettingsValues} from "@app/core/state"
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
event: any
|
event: any
|
||||||
@@ -68,7 +68,7 @@
|
|||||||
|
|
||||||
if (!parsed || hideMediaAtDepth <= depth) return false
|
if (!parsed || hideMediaAtDepth <= depth) return false
|
||||||
|
|
||||||
if (isLink(parsed) && $userSettingValues.show_media && isStartOrEnd(i)) {
|
if (isLink(parsed) && $userSettingsValues.show_media && isStartOrEnd(i)) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,7 +101,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
let warning = $state(
|
let warning = $state(
|
||||||
$userSettingValues.hide_sensitive && event.tags.find(nthEq(0, "content-warning"))?.[1],
|
$userSettingsValues.hide_sensitive && event.tags.find(nthEq(0, "content-warning"))?.[1],
|
||||||
)
|
)
|
||||||
|
|
||||||
const shortContent = $derived(
|
const shortContent = $derived(
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import Link from "@lib/components/Link.svelte"
|
||||||
|
import Button from "@lib/components/Button.svelte"
|
||||||
|
import ModalHeader from "@lib/components/ModalHeader.svelte"
|
||||||
|
|
||||||
|
const back = () => history.back()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="column gap-4">
|
||||||
|
<ModalHeader>
|
||||||
|
{#snippet title()}
|
||||||
|
<div>What are digital signatures?</div>
|
||||||
|
{/snippet}
|
||||||
|
</ModalHeader>
|
||||||
|
<p>
|
||||||
|
Most online services ask their users to trust them that they're being honest, and they usually
|
||||||
|
are. However, traditional social media platforms have the ability to <strong
|
||||||
|
>create forged content</strong> that can appear to be genuinely authored, but which are actually
|
||||||
|
counterfeit.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
On <Link external href="https://nostr.com/">Nostr</Link>, all your content is authenticated
|
||||||
|
using <strong>digital signatures</strong>, which cryptographically tie a particular person to a
|
||||||
|
given post or message.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
The result is that you don't normally have to trust service providers not to tamper with the
|
||||||
|
information flowing through the network — instead, your client software can prove that a given
|
||||||
|
piece of data is authentic.
|
||||||
|
</p>
|
||||||
|
<Button class="btn btn-primary" onclick={back}>Got it</Button>
|
||||||
|
</div>
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
import Icon from "@lib/components/Icon.svelte"
|
import Icon from "@lib/components/Icon.svelte"
|
||||||
import ModalHeader from "@lib/components/ModalHeader.svelte"
|
import ModalHeader from "@lib/components/ModalHeader.svelte"
|
||||||
import ModalFooter from "@lib/components/ModalFooter.svelte"
|
import ModalFooter from "@lib/components/ModalFooter.svelte"
|
||||||
import {removeSpaceMembership} from "@app/core/commands"
|
import {removeSpaceMembership, removeTrustedRelay} from "@app/core/commands"
|
||||||
|
|
||||||
const {url} = $props()
|
const {url} = $props()
|
||||||
|
|
||||||
@@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
await removeSpaceMembership(url)
|
await removeSpaceMembership(url)
|
||||||
|
await removeTrustedRelay(url)
|
||||||
} finally {
|
} finally {
|
||||||
loading = false
|
loading = false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,86 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import {goto} from "$app/navigation"
|
||||||
|
import {remove} from "@welshman/lib"
|
||||||
|
import {displayRelayUrl} from "@welshman/util"
|
||||||
|
import {preventDefault} from "@lib/html"
|
||||||
|
import Icon from "@lib/components/Icon.svelte"
|
||||||
|
import Spinner from "@lib/components/Spinner.svelte"
|
||||||
|
import Button from "@lib/components/Button.svelte"
|
||||||
|
import ModalHeader from "@lib/components/ModalHeader.svelte"
|
||||||
|
import InfoSignatures from "@app/components/InfoSignatures.svelte"
|
||||||
|
import {relaysPendingTrust} from "@app/core/state"
|
||||||
|
import {removeSpaceMembership, addTrustedRelay, removeTrustedRelay} from "@app/core/commands"
|
||||||
|
import {pushModal} from "@app/util/modal"
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
url: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const {url}: Props = $props()
|
||||||
|
|
||||||
|
const showInfoSignatures = () => pushModal(InfoSignatures)
|
||||||
|
|
||||||
|
const untrustSpace = async () => {
|
||||||
|
loading = true
|
||||||
|
|
||||||
|
try {
|
||||||
|
await removeSpaceMembership(url)
|
||||||
|
await removeTrustedRelay(url)
|
||||||
|
goto("/")
|
||||||
|
} finally {
|
||||||
|
loading = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const trustSpace = async () => {
|
||||||
|
loading = true
|
||||||
|
|
||||||
|
try {
|
||||||
|
await addTrustedRelay(url)
|
||||||
|
|
||||||
|
relaysPendingTrust.update($r => remove(url, $r))
|
||||||
|
} finally {
|
||||||
|
loading = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let loading = $state(false)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<form class="column gap-4" onsubmit={preventDefault(trustSpace)}>
|
||||||
|
<ModalHeader>
|
||||||
|
{#snippet title()}
|
||||||
|
Do you trust this space?
|
||||||
|
{/snippet}
|
||||||
|
{#snippet info()}
|
||||||
|
<div>
|
||||||
|
Only join <span class="text-primary">{displayRelayUrl(url)}</span> if you trust the adminstrator
|
||||||
|
</div>
|
||||||
|
{/snippet}
|
||||||
|
</ModalHeader>
|
||||||
|
<div class="m-auto flex flex-col gap-4">
|
||||||
|
<p>
|
||||||
|
This space has opted not to publish <Button class="link" onclick={showInfoSignatures}
|
||||||
|
>digital signatures</Button
|
||||||
|
>, which means that they have the ability to forge messages from other users.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
If you trust this space's admin, you can continue. Otherwise, it may be safer not to join this
|
||||||
|
space.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="mt-4 flex flex-col gap-2 sm:flex-row sm:justify-between">
|
||||||
|
<Button class="btn btn-neutral" onclick={untrustSpace} disabled={loading}>
|
||||||
|
{#if !loading}
|
||||||
|
<Icon icon="close-circle" />
|
||||||
|
{/if}
|
||||||
|
<Spinner {loading}>I don't trust this space</Spinner>
|
||||||
|
</Button>
|
||||||
|
<Button type="submit" class="btn btn-primary" disabled={loading}>
|
||||||
|
{#if !loading}
|
||||||
|
<Icon icon="check-circle" />
|
||||||
|
{/if}
|
||||||
|
<Spinner {loading}>I trust this space, continue</Spinner>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
@@ -1,7 +1,17 @@
|
|||||||
import {nwc} from "@getalby/sdk"
|
import {nwc} from "@getalby/sdk"
|
||||||
import * as nip19 from "nostr-tools/nip19"
|
import * as nip19 from "nostr-tools/nip19"
|
||||||
import {get} from "svelte/store"
|
import {get} from "svelte/store"
|
||||||
import {randomId, flatten, poll, uniq, equals, TIMEZONE, LOCALE} from "@welshman/lib"
|
import {
|
||||||
|
randomId,
|
||||||
|
append,
|
||||||
|
remove,
|
||||||
|
flatten,
|
||||||
|
poll,
|
||||||
|
uniq,
|
||||||
|
equals,
|
||||||
|
TIMEZONE,
|
||||||
|
LOCALE,
|
||||||
|
} from "@welshman/lib"
|
||||||
import type {Feed} from "@welshman/feeds"
|
import type {Feed} from "@welshman/feeds"
|
||||||
import type {TrustedEvent, EventContent} from "@welshman/util"
|
import type {TrustedEvent, EventContent} from "@welshman/util"
|
||||||
import {
|
import {
|
||||||
@@ -19,6 +29,7 @@ import {
|
|||||||
ALERT_WEB,
|
ALERT_WEB,
|
||||||
ALERT_IOS,
|
ALERT_IOS,
|
||||||
ALERT_ANDROID,
|
ALERT_ANDROID,
|
||||||
|
APP_DATA,
|
||||||
isSignedEvent,
|
isSignedEvent,
|
||||||
makeEvent,
|
makeEvent,
|
||||||
displayProfile,
|
displayProfile,
|
||||||
@@ -56,13 +67,16 @@ import {
|
|||||||
tagEventForQuote,
|
tagEventForQuote,
|
||||||
getThunkError,
|
getThunkError,
|
||||||
} from "@welshman/app"
|
} from "@welshman/app"
|
||||||
|
import type {SettingsValues} from "@app/core/state"
|
||||||
import {
|
import {
|
||||||
|
SETTINGS,
|
||||||
PROTECTED,
|
PROTECTED,
|
||||||
userMembership,
|
userMembership,
|
||||||
INDEXER_RELAYS,
|
INDEXER_RELAYS,
|
||||||
NOTIFIER_PUBKEY,
|
NOTIFIER_PUBKEY,
|
||||||
NOTIFIER_RELAY,
|
NOTIFIER_RELAY,
|
||||||
userRoomsByUrl,
|
userRoomsByUrl,
|
||||||
|
userSettingsValues,
|
||||||
} from "@app/core/state"
|
} from "@app/core/state"
|
||||||
|
|
||||||
// Utils
|
// Utils
|
||||||
@@ -461,6 +475,25 @@ export const makeAlert = async (params: AlertParams) => {
|
|||||||
export const publishAlert = async (params: AlertParams) =>
|
export const publishAlert = async (params: AlertParams) =>
|
||||||
publishThunk({event: await makeAlert(params), relays: [NOTIFIER_RELAY]})
|
publishThunk({event: await makeAlert(params), relays: [NOTIFIER_RELAY]})
|
||||||
|
|
||||||
|
// Settings
|
||||||
|
|
||||||
|
export const makeSettings = async (params: Partial<SettingsValues>) => {
|
||||||
|
const json = JSON.stringify({...userSettingsValues.get(), ...params})
|
||||||
|
const content = await signer.get().nip44.encrypt(pubkey.get()!, json)
|
||||||
|
const tags = [["d", SETTINGS]]
|
||||||
|
|
||||||
|
return makeEvent(APP_DATA, {content, tags})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const publishSettings = async (params: Partial<SettingsValues>) =>
|
||||||
|
publishThunk({event: await makeSettings(params), relays: Router.get().FromUser().getUrls()})
|
||||||
|
|
||||||
|
export const addTrustedRelay = async (url: string) =>
|
||||||
|
publishSettings({trusted_relays: append(url, userSettingsValues.get().trusted_relays)})
|
||||||
|
|
||||||
|
export const removeTrustedRelay = async (url: string) =>
|
||||||
|
publishSettings({trusted_relays: remove(url, userSettingsValues.get().trusted_relays)})
|
||||||
|
|
||||||
// Lightning
|
// Lightning
|
||||||
|
|
||||||
export const getWebLn = () => (window as any).webln
|
export const getWebLn = () => (window as any).webln
|
||||||
|
|||||||
+28
-15
@@ -1,6 +1,6 @@
|
|||||||
import twColors from "tailwindcss/colors"
|
import twColors from "tailwindcss/colors"
|
||||||
import {Capacitor} from "@capacitor/core"
|
import {Capacitor} from "@capacitor/core"
|
||||||
import {get, derived} from "svelte/store"
|
import {get, derived, writable} from "svelte/store"
|
||||||
import * as nip19 from "nostr-tools/nip19"
|
import * as nip19 from "nostr-tools/nip19"
|
||||||
import {
|
import {
|
||||||
on,
|
on,
|
||||||
@@ -23,7 +23,7 @@ import {
|
|||||||
always,
|
always,
|
||||||
} from "@welshman/lib"
|
} from "@welshman/lib"
|
||||||
import type {Socket} from "@welshman/net"
|
import type {Socket} from "@welshman/net"
|
||||||
import {Pool, load, AuthStateEvent, SocketEvent} from "@welshman/net"
|
import {Pool, load, AuthStateEvent, SocketEvent, netContext} from "@welshman/net"
|
||||||
import {
|
import {
|
||||||
collection,
|
collection,
|
||||||
custom,
|
custom,
|
||||||
@@ -56,6 +56,7 @@ import {
|
|||||||
ALERT_IOS,
|
ALERT_IOS,
|
||||||
ALERT_ANDROID,
|
ALERT_ANDROID,
|
||||||
ALERT_STATUS,
|
ALERT_STATUS,
|
||||||
|
APP_DATA,
|
||||||
getGroupTags,
|
getGroupTags,
|
||||||
getRelayTagValues,
|
getRelayTagValues,
|
||||||
getPubkeyTagValues,
|
getPubkeyTagValues,
|
||||||
@@ -68,6 +69,7 @@ import {
|
|||||||
getTag,
|
getTag,
|
||||||
getTagValue,
|
getTagValue,
|
||||||
getTagValues,
|
getTagValues,
|
||||||
|
verifyEvent,
|
||||||
} from "@welshman/util"
|
} from "@welshman/util"
|
||||||
import type {TrustedEvent, SignedEvent, PublishedList, List, Filter} from "@welshman/util"
|
import type {TrustedEvent, SignedEvent, PublishedList, List, Filter} from "@welshman/util"
|
||||||
import {Nip59, decrypt} from "@welshman/signer"
|
import {Nip59, decrypt} from "@welshman/signer"
|
||||||
@@ -304,6 +306,9 @@ appContext.dufflepudUrl = DUFFLEPUD_URL
|
|||||||
|
|
||||||
routerContext.getIndexerRelays = always(INDEXER_RELAYS)
|
routerContext.getIndexerRelays = always(INDEXER_RELAYS)
|
||||||
|
|
||||||
|
netContext.isEventValid = (event: TrustedEvent, url: string) =>
|
||||||
|
getSetting<string[]>("trusted_relays").includes(url) || verifyEvent(event)
|
||||||
|
|
||||||
// Settings
|
// Settings
|
||||||
|
|
||||||
export const canDecrypt = synced({
|
export const canDecrypt = synced({
|
||||||
@@ -312,23 +317,27 @@ export const canDecrypt = synced({
|
|||||||
storage: localStorageProvider,
|
storage: localStorageProvider,
|
||||||
})
|
})
|
||||||
|
|
||||||
export const SETTINGS = 38489
|
export const SETTINGS = "flotilla/settings"
|
||||||
|
|
||||||
|
export type SettingsValues = {
|
||||||
|
show_media: boolean
|
||||||
|
hide_sensitive: boolean
|
||||||
|
trusted_relays: string[]
|
||||||
|
report_usage: boolean
|
||||||
|
report_errors: boolean
|
||||||
|
send_delay: number
|
||||||
|
font_size: number
|
||||||
|
}
|
||||||
|
|
||||||
export type Settings = {
|
export type Settings = {
|
||||||
event: TrustedEvent
|
event: TrustedEvent
|
||||||
values: {
|
values: SettingsValues
|
||||||
show_media: boolean
|
|
||||||
hide_sensitive: boolean
|
|
||||||
report_usage: boolean
|
|
||||||
report_errors: boolean
|
|
||||||
send_delay: number
|
|
||||||
font_size: number
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const defaultSettings = {
|
export const defaultSettings = {
|
||||||
show_media: true,
|
show_media: true,
|
||||||
hide_sensitive: true,
|
hide_sensitive: true,
|
||||||
|
trusted_relays: [],
|
||||||
report_usage: true,
|
report_usage: true,
|
||||||
report_errors: true,
|
report_errors: true,
|
||||||
send_delay: 3000,
|
send_delay: 3000,
|
||||||
@@ -336,7 +345,7 @@ export const defaultSettings = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const settings = deriveEventsMapped<Settings>(repository, {
|
export const settings = deriveEventsMapped<Settings>(repository, {
|
||||||
filters: [{kinds: [SETTINGS]}],
|
filters: [{kinds: [APP_DATA], "#d": [SETTINGS]}],
|
||||||
itemToEvent: item => item.event,
|
itemToEvent: item => item.event,
|
||||||
eventToItem: async (event: TrustedEvent) => ({
|
eventToItem: async (event: TrustedEvent) => ({
|
||||||
event,
|
event,
|
||||||
@@ -352,9 +361,13 @@ export const {
|
|||||||
name: "settings",
|
name: "settings",
|
||||||
store: settings,
|
store: settings,
|
||||||
getKey: settings => settings.event.pubkey,
|
getKey: settings => settings.event.pubkey,
|
||||||
load: makeOutboxLoader(SETTINGS),
|
load: makeOutboxLoader(APP_DATA, {"#d": [SETTINGS]}),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Relays sending events with empty signatures that the user has to choose to trust
|
||||||
|
|
||||||
|
export const relaysPendingTrust = writable<string[]>([])
|
||||||
|
|
||||||
// Alerts
|
// Alerts
|
||||||
|
|
||||||
export type Alert = {
|
export type Alert = {
|
||||||
@@ -610,11 +623,11 @@ export const userSettings = withGetter(
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
export const userSettingValues = withGetter(
|
export const userSettingsValues = withGetter(
|
||||||
derived(userSettings, $s => $s?.values || defaultSettings),
|
derived(userSettings, $s => $s?.values || defaultSettings),
|
||||||
)
|
)
|
||||||
|
|
||||||
export const getSetting = <T>(key: keyof Settings["values"]) => userSettingValues.get()[key] as T
|
export const getSetting = <T>(key: keyof Settings["values"]) => userSettingsValues.get()[key] as T
|
||||||
|
|
||||||
export const userMembership = withGetter(
|
export const userMembership = withGetter(
|
||||||
derived([pubkey, membershipsByPubkey], ([$pubkey, $membershipsByPubkey]) => {
|
derived([pubkey, membershipsByPubkey], ([$pubkey, $membershipsByPubkey]) => {
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import type {Snippet} from "svelte"
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
children?: import("svelte").Snippet
|
children?: Snippet
|
||||||
}
|
}
|
||||||
|
|
||||||
const {children}: Props = $props()
|
const {children}: Props = $props()
|
||||||
|
|||||||
@@ -9,11 +9,23 @@
|
|||||||
import {dev} from "$app/environment"
|
import {dev} from "$app/environment"
|
||||||
import {goto} from "$app/navigation"
|
import {goto} from "$app/navigation"
|
||||||
import {sync, localStorageProvider} from "@welshman/store"
|
import {sync, localStorageProvider} from "@welshman/store"
|
||||||
import {identity, memoize, spec, sleep, defer, ago, WEEK, TaskQueue} from "@welshman/lib"
|
import {
|
||||||
|
identity,
|
||||||
|
call,
|
||||||
|
memoize,
|
||||||
|
spec,
|
||||||
|
sleep,
|
||||||
|
on,
|
||||||
|
defer,
|
||||||
|
ago,
|
||||||
|
WEEK,
|
||||||
|
TaskQueue,
|
||||||
|
} from "@welshman/lib"
|
||||||
import type {TrustedEvent, StampedEvent} from "@welshman/util"
|
import type {TrustedEvent, StampedEvent} from "@welshman/util"
|
||||||
import {
|
import {
|
||||||
WRAP,
|
WRAP,
|
||||||
EVENT_TIME,
|
EVENT_TIME,
|
||||||
|
APP_DATA,
|
||||||
THREAD,
|
THREAD,
|
||||||
MESSAGE,
|
MESSAGE,
|
||||||
INBOX_RELAYS,
|
INBOX_RELAYS,
|
||||||
@@ -28,8 +40,14 @@
|
|||||||
getRelaysFromList,
|
getRelaysFromList,
|
||||||
} from "@welshman/util"
|
} from "@welshman/util"
|
||||||
import {Nip46Broker, makeSecret} from "@welshman/signer"
|
import {Nip46Broker, makeSecret} from "@welshman/signer"
|
||||||
import type {Socket} from "@welshman/net"
|
import type {Socket, RelayMessage} from "@welshman/net"
|
||||||
import {request, defaultSocketPolicies, makeSocketPolicyAuth} from "@welshman/net"
|
import {
|
||||||
|
request,
|
||||||
|
defaultSocketPolicies,
|
||||||
|
makeSocketPolicyAuth,
|
||||||
|
SocketEvent,
|
||||||
|
isRelayEvent,
|
||||||
|
} from "@welshman/net"
|
||||||
import {
|
import {
|
||||||
loadRelay,
|
loadRelay,
|
||||||
db,
|
db,
|
||||||
@@ -64,9 +82,11 @@
|
|||||||
import {
|
import {
|
||||||
INDEXER_RELAYS,
|
INDEXER_RELAYS,
|
||||||
userMembership,
|
userMembership,
|
||||||
userSettingValues,
|
userSettingsValues,
|
||||||
|
relaysPendingTrust,
|
||||||
ensureUnwrapped,
|
ensureUnwrapped,
|
||||||
canDecrypt,
|
canDecrypt,
|
||||||
|
getSetting,
|
||||||
} from "@app/core/state"
|
} from "@app/core/state"
|
||||||
import {loadUserData, listenForNotifications} from "@app/core/requests"
|
import {loadUserData, listenForNotifications} from "@app/core/requests"
|
||||||
import {theme} from "@app/util/theme"
|
import {theme} from "@app/util/theme"
|
||||||
@@ -162,9 +182,9 @@
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Sync font size
|
// Sync font size
|
||||||
userSettingValues.subscribe($userSettingValues => {
|
userSettingsValues.subscribe($userSettingsValues => {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
document.documentElement.style["font-size"] = `${$userSettingValues.font_size}rem`
|
document.documentElement.style["font-size"] = `${$userSettingsValues.font_size}rem`
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!db) {
|
if (!db) {
|
||||||
@@ -214,9 +234,16 @@
|
|||||||
repository,
|
repository,
|
||||||
rankEvent: (e: TrustedEvent) => {
|
rankEvent: (e: TrustedEvent) => {
|
||||||
if (
|
if (
|
||||||
[PROFILE, FOLLOWS, MUTES, RELAYS, BLOSSOM_SERVERS, INBOX_RELAYS, ROOMS].includes(
|
[
|
||||||
e.kind,
|
PROFILE,
|
||||||
)
|
FOLLOWS,
|
||||||
|
MUTES,
|
||||||
|
RELAYS,
|
||||||
|
BLOSSOM_SERVERS,
|
||||||
|
INBOX_RELAYS,
|
||||||
|
ROOMS,
|
||||||
|
APP_DATA,
|
||||||
|
].includes(e.kind)
|
||||||
) {
|
) {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
@@ -239,6 +266,40 @@
|
|||||||
sign: (event: StampedEvent) => signer.get()?.sign(event),
|
sign: (event: StampedEvent) => signer.get()?.sign(event),
|
||||||
shouldAuth: (socket: Socket) => true,
|
shouldAuth: (socket: Socket) => true,
|
||||||
}),
|
}),
|
||||||
|
(socket: Socket) => {
|
||||||
|
const buffer: RelayMessage[] = []
|
||||||
|
|
||||||
|
const unsubscribers = [
|
||||||
|
// When the socket goes from untrusted to trusted, receive all buffered messages
|
||||||
|
userSettingsValues.subscribe($settings => {
|
||||||
|
if ($settings.trusted_relays.includes(socket.url)) {
|
||||||
|
for (const message of buffer.splice(0)) {
|
||||||
|
socket._recvQueue.push(message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
// When we get an event with no signature from an untrusted relay, remove it from
|
||||||
|
// the receive queue. If trust status is undefined, buffer it for later.
|
||||||
|
on(socket, SocketEvent.Receiving, (message: RelayMessage) => {
|
||||||
|
if (isRelayEvent(message) && !message[2]?.sig) {
|
||||||
|
const isTrusted = getSetting<string[]>("trusted_relays").includes(socket.url)
|
||||||
|
|
||||||
|
if (!isTrusted) {
|
||||||
|
socket._recvQueue.remove(message)
|
||||||
|
buffer.push(message)
|
||||||
|
|
||||||
|
if (!$relaysPendingTrust.includes(socket.url)) {
|
||||||
|
relaysPendingTrust.update($r => [...$r, socket.url])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
unsubscribers.forEach(call)
|
||||||
|
}
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
// Load relay info
|
// Load relay info
|
||||||
|
|||||||
@@ -9,14 +9,7 @@
|
|||||||
BLOSSOM_SERVERS,
|
BLOSSOM_SERVERS,
|
||||||
} from "@welshman/util"
|
} from "@welshman/util"
|
||||||
import {Router} from "@welshman/router"
|
import {Router} from "@welshman/router"
|
||||||
import {
|
import {userMutes, tagPubkey, publishThunk, userBlossomServers} from "@welshman/app"
|
||||||
pubkey,
|
|
||||||
signer,
|
|
||||||
userMutes,
|
|
||||||
tagPubkey,
|
|
||||||
publishThunk,
|
|
||||||
userBlossomServers,
|
|
||||||
} from "@welshman/app"
|
|
||||||
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"
|
||||||
@@ -24,38 +17,32 @@
|
|||||||
import Button from "@lib/components/Button.svelte"
|
import Button from "@lib/components/Button.svelte"
|
||||||
import ProfileMultiSelect from "@app/components/ProfileMultiSelect.svelte"
|
import ProfileMultiSelect from "@app/components/ProfileMultiSelect.svelte"
|
||||||
import {pushToast} from "@app/util/toast"
|
import {pushToast} from "@app/util/toast"
|
||||||
import {SETTINGS, PLATFORM_NAME, userSettingValues} from "@app/core/state"
|
import {PLATFORM_NAME, userSettingsValues} from "@app/core/state"
|
||||||
|
import {publishSettings} from "@app/core/commands"
|
||||||
|
|
||||||
const reset = () => {
|
const reset = () => {
|
||||||
settings = {...$userSettingValues}
|
settings = {...$userSettingsValues}
|
||||||
mutedPubkeys = getPubkeyTagValues(getListTags($userMutes))
|
mutedPubkeys = getPubkeyTagValues(getListTags($userMutes))
|
||||||
blossomServers = getTagValues("server", getListTags($userBlossomServers))
|
blossomServers = getTagValues("server", getListTags($userBlossomServers))
|
||||||
}
|
}
|
||||||
|
|
||||||
const onsubmit = preventDefault(async () => {
|
const onsubmit = preventDefault(async () => {
|
||||||
const json = JSON.stringify($state.snapshot(settings))
|
await publishSettings($state.snapshot(settings))
|
||||||
const content = await $signer!.nip44.encrypt($pubkey!, json)
|
|
||||||
const relays = Router.get().FromUser().getUrls()
|
|
||||||
|
|
||||||
publishThunk({
|
|
||||||
event: makeEvent(SETTINGS, {content}),
|
|
||||||
relays,
|
|
||||||
})
|
|
||||||
|
|
||||||
publishThunk({
|
publishThunk({
|
||||||
event: makeEvent(MUTES, {tags: mutedPubkeys.map(tagPubkey)}),
|
event: makeEvent(MUTES, {tags: mutedPubkeys.map(tagPubkey)}),
|
||||||
relays,
|
relays: Router.get().FromUser().getUrls(),
|
||||||
})
|
})
|
||||||
|
|
||||||
publishThunk({
|
publishThunk({
|
||||||
event: makeEvent(BLOSSOM_SERVERS, {tags: blossomServers.map(tagger("server"))}),
|
event: makeEvent(BLOSSOM_SERVERS, {tags: blossomServers.map(tagger("server"))}),
|
||||||
relays,
|
relays: Router.get().FromUser().getUrls(),
|
||||||
})
|
})
|
||||||
|
|
||||||
pushToast({message: "Your settings have been saved!"})
|
pushToast({message: "Your settings have been saved!"})
|
||||||
})
|
})
|
||||||
|
|
||||||
let settings = $state({...$userSettingValues})
|
let settings = $state({...$userSettingsValues})
|
||||||
let mutedPubkeys = $state(getPubkeyTagValues(getListTags($userMutes)))
|
let mutedPubkeys = $state(getPubkeyTagValues(getListTags($userMutes)))
|
||||||
let blossomServers = $state(getTagValues("server", getListTags($userBlossomServers)))
|
let blossomServers = $state(getTagValues("server", getListTags($userBlossomServers)))
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -5,14 +5,16 @@
|
|||||||
import {ago, MONTH} from "@welshman/lib"
|
import {ago, MONTH} from "@welshman/lib"
|
||||||
import {ROOM_META, EVENT_TIME, THREAD, COMMENT, MESSAGE} from "@welshman/util"
|
import {ROOM_META, EVENT_TIME, THREAD, COMMENT, MESSAGE} from "@welshman/util"
|
||||||
import Page from "@lib/components/Page.svelte"
|
import Page from "@lib/components/Page.svelte"
|
||||||
|
import Dialog from "@lib/components/Dialog.svelte"
|
||||||
import SecondaryNav from "@lib/components/SecondaryNav.svelte"
|
import SecondaryNav from "@lib/components/SecondaryNav.svelte"
|
||||||
import MenuSpace from "@app/components/MenuSpace.svelte"
|
import MenuSpace from "@app/components/MenuSpace.svelte"
|
||||||
import SpaceAuthError from "@app/components/SpaceAuthError.svelte"
|
import SpaceAuthError from "@app/components/SpaceAuthError.svelte"
|
||||||
|
import SpaceTrustRelay from "@app/components/SpaceTrustRelay.svelte"
|
||||||
import {pushToast} from "@app/util/toast"
|
import {pushToast} from "@app/util/toast"
|
||||||
import {pushModal} from "@app/util/modal"
|
import {pushModal} from "@app/util/modal"
|
||||||
import {setChecked} from "@app/util/notifications"
|
import {setChecked} from "@app/util/notifications"
|
||||||
import {checkRelayConnection, checkRelayAuth, checkRelayAccess} from "@app/core/commands"
|
import {checkRelayConnection, checkRelayAuth, checkRelayAccess} from "@app/core/commands"
|
||||||
import {decodeRelay, userRoomsByUrl} from "@app/core/state"
|
import {decodeRelay, userRoomsByUrl, relaysPendingTrust} from "@app/core/state"
|
||||||
import {pullConservatively} from "@app/core/requests"
|
import {pullConservatively} from "@app/core/requests"
|
||||||
import {notifications} from "@app/util/notifications"
|
import {notifications} from "@app/util/notifications"
|
||||||
|
|
||||||
@@ -82,3 +84,9 @@
|
|||||||
{@render children?.()}
|
{@render children?.()}
|
||||||
{/key}
|
{/key}
|
||||||
</Page>
|
</Page>
|
||||||
|
|
||||||
|
{#if $relaysPendingTrust.includes(url)}
|
||||||
|
<Dialog>
|
||||||
|
<SpaceTrustRelay {url} />
|
||||||
|
</Dialog>
|
||||||
|
{/if}
|
||||||
|
|||||||
@@ -31,7 +31,7 @@
|
|||||||
import ChannelComposeParent from "@app/components/ChannelComposeParent.svelte"
|
import ChannelComposeParent from "@app/components/ChannelComposeParent.svelte"
|
||||||
import {
|
import {
|
||||||
userRoomsByUrl,
|
userRoomsByUrl,
|
||||||
userSettingValues,
|
userSettingsValues,
|
||||||
decodeRelay,
|
decodeRelay,
|
||||||
getEventsForUrl,
|
getEventsForUrl,
|
||||||
deriveUserMembershipStatus,
|
deriveUserMembershipStatus,
|
||||||
@@ -128,7 +128,7 @@
|
|||||||
const thunk = publishThunk({
|
const thunk = publishThunk({
|
||||||
relays: [url],
|
relays: [url],
|
||||||
event: makeEvent(MESSAGE, template),
|
event: makeEvent(MESSAGE, template),
|
||||||
delay: $userSettingValues.send_delay,
|
delay: $userSettingsValues.send_delay,
|
||||||
})
|
})
|
||||||
|
|
||||||
pushToast({
|
pushToast({
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
import ChannelCompose from "@app/components/ChannelCompose.svelte"
|
import ChannelCompose from "@app/components/ChannelCompose.svelte"
|
||||||
import ChannelComposeParent from "@app/components/ChannelComposeParent.svelte"
|
import ChannelComposeParent from "@app/components/ChannelComposeParent.svelte"
|
||||||
import {
|
import {
|
||||||
userSettingValues,
|
userSettingsValues,
|
||||||
decodeRelay,
|
decodeRelay,
|
||||||
getEventsForUrl,
|
getEventsForUrl,
|
||||||
PROTECTED,
|
PROTECTED,
|
||||||
@@ -69,7 +69,7 @@
|
|||||||
const thunk = publishThunk({
|
const thunk = publishThunk({
|
||||||
relays: [url],
|
relays: [url],
|
||||||
event: makeEvent(MESSAGE, template),
|
event: makeEvent(MESSAGE, template),
|
||||||
delay: $userSettingValues.send_delay,
|
delay: $userSettingsValues.send_delay,
|
||||||
})
|
})
|
||||||
|
|
||||||
pushToast({
|
pushToast({
|
||||||
|
|||||||
Reference in New Issue
Block a user