Replace the Access class with plain join functions, move invite preparation into InviteLink and space notification opt-in into push
This commit is contained in:
parent
b924523455
commit
f1946b9a55
8 changed files with 123 additions and 216 deletions
|
|
@ -144,19 +144,23 @@ has no getter for them yet. Roles are only ever changed over
|
|||
NIP-86 (`createRole`, `editRole`, `deleteRole`, `assignRole`, `unassignRole`), never by publishing
|
||||
33534.
|
||||
|
||||
Joining a space (`attemptRelayAccess` and `Access` in `src/app/access.ts`):
|
||||
Joining a space (`attemptRelayAccess` and `completeSpaceJoin` in `src/app/access.ts`):
|
||||
|
||||
1. Open the socket and drive NIP-42 auth, retrying up to three times.
|
||||
2. Publish `RelayJoin` with the claim. The writer protects the event and requires a forced relay.
|
||||
3. Translate refusals: "invite code" means rejected, "claim" means the space needs an invite.
|
||||
4. `completeJoin`: `roomLists.addRelay(url)`, restart sync, and `Sync.push` the user's `RELAYS`,
|
||||
3. Translate refusals: a `mute:` reply (`matchReason`) is ignored, and the free-text "invite code"
|
||||
means rejected and "claim" means the space needs an invite.
|
||||
4. `completeSpaceJoin`: opt into the space's notifications (`optInSpaceNotifications` in
|
||||
`src/app/push`, which asks for push permission first when push is on), `roomLists.addRelay(url)`,
|
||||
clear the relay from `relaysMostlyRestricted`, restart sync, and `Sync.push` the user's `RELAYS`,
|
||||
`MESSAGING_RELAYS`, `FOLLOWS` and `PROFILE` to the space so other members can see them.
|
||||
|
||||
Invite links are `${PLATFORM_URL}/join?r=<relay>&c=<claim>`, plus `h` and `code` for a room
|
||||
(`makeInviteLink`; `parseInviteLink` also accepts a bare relay URL). `src/routes/join` renders
|
||||
`SpaceInviteAccept`, which calls `Access.acceptInvite` to join the space and then the room.
|
||||
`Access.prepareInvite` gets a claim over NIP-86 (`supportedmethods`, then `listclaims`, then
|
||||
`createclaim`), and `InviteLink` renders the result for both `SpaceInvite` and `RoomInvite`; this replaced reading `RELAY_INVITE` events. Leaving (`SpaceExit`,
|
||||
`SpaceInviteAccept`, which calls `acceptInvite` to join the space (skipped if already joined) and
|
||||
then the room. `InviteLink`, rendered by both `SpaceInvite` and `RoomInvite`, gets a claim over
|
||||
NIP-86 (`supportedmethods`, then `listclaims`, then `createclaim`), publishes a room invite code
|
||||
for a room, and holds that state itself; this replaced reading `RELAY_INVITE` events. Leaving (`SpaceExit`,
|
||||
`SpaceAuthError`) is `roomLists.removeRelay(url)` plus `publishLeaveRequest(url)`.
|
||||
|
||||
## NIP-86 relay management
|
||||
|
|
@ -171,7 +175,7 @@ relay's URL, each call signed with a fresh NIP-98 event. Every method resolves t
|
|||
| `banPubkey`, `unbanPubkey`, `allowPubkey`, `unallowPubkey`, `listBannedPubkeys` | `ProfileDetail`, `SpaceMemberMenu`, `SpaceMemberBannedMenu`, `ReportMenu`, `allowPubkeys` (`SpaceMembersAdd`, `RoleAddMembers`, `addRoomMembers`) |
|
||||
| `banEvent` | `RoomItemMenu`, `EventMenu`, `ReportMenu`, `RoomJoinItem` (dismissing a join request) |
|
||||
| `createRole`, `editRole`, `deleteRole`, `assignRole`, `unassignRole` | `RoleCreate`, `RoleEdit`, `SpaceRoleMenu`, `SpaceMemberRoles` (via `syncAssignments`), `RoleAddMembers` |
|
||||
| `listClaims`, `createClaim` | `Access.prepareInvite` |
|
||||
| `listClaims`, `createClaim` | `InviteLink` |
|
||||
| `changeRelayName`, `changeRelayDescription`, `changeRelayIcon` | `SpaceEdit` |
|
||||
|
||||
A relay answers `supportedmethods` with what the authenticated pubkey may call, so every control
|
||||
|
|
@ -179,7 +183,7 @@ is gated on the method behind it through `deriveSpacePermissions(url)`, a store
|
|||
(`$permissions.ban`, `$permissions.editRoles`). The composite gates a parent uses to decide whether
|
||||
to render a menu (`memberMenu`, `roleMenu`, `bannedMenu`, `directoryMenu`) are defined there too,
|
||||
so parent and menu can't disagree. The `SpaceManagement` plugin (`spaceManagement`) caches the
|
||||
methods per app, re-checked at most every five minutes per URL, and `Access.prepareInvite` reads
|
||||
methods per app, re-checked at most every five minutes per URL, and `InviteLink` reads
|
||||
them through `loadSupportedMethods`. It also holds the admin and ban lists, which
|
||||
`loadMethodAssignees`/`loadBannedPubkeys` refresh after a change. Still handle an error from the
|
||||
call, since a listed method can be refused for a particular event or target.
|
||||
|
|
|
|||
|
|
@ -322,7 +322,7 @@ its composer has gone.
|
|||
## Background sync (`src/app/sync.ts`)
|
||||
|
||||
The root layout calls `syncApplicationData()` once the session is restored and storage is ready,
|
||||
and again after every app swap. `Access.completeJoin` calls it after a space is joined. Each
|
||||
and again after every app swap. `completeSpaceJoin` in `access.ts` calls it after a space is joined. Each
|
||||
call tears down the previous run.
|
||||
|
||||
- `syncRelays` loads NIP-11 for the indexer relays, the current route's relay and the user's
|
||||
|
|
@ -410,9 +410,8 @@ NIP-86 calls (`relayManagement.get().forUrl(url)`) are not thunks. They return
|
|||
- **Singleton session state.** `call.ts` keeps call state in plain writables (`callState`,
|
||||
`currentCallSession`, …).
|
||||
- **UI signals.** `toast` in `toast.ts`, and `relaysPendingTrust` in `policies.ts`.
|
||||
- **A controller per flow.** `Access` (`access.ts`) and `Nip46Controller` (`nip46.ts`) are
|
||||
classes a component instantiates (`new Access(url)`). They hold the writables and actions for
|
||||
a multi-step flow.
|
||||
- **A controller per flow.** `Nip46Controller` (`nip46.ts`) is a class a component instantiates.
|
||||
It holds the writables and actions for a multi-step flow.
|
||||
- **Module-owned values.** `wallet` in `lightning.ts` is a `withGetter(writable(...))` that the
|
||||
root layout persists.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import {derived, writable, type Readable} from "svelte/store"
|
||||
import {dissoc, fromPairs, last, poll, randomId, sleep, tryCatch} from "@welshman/lib"
|
||||
import {AuthStatus, SocketStatus} from "@welshman/net"
|
||||
import {derived} from "svelte/store"
|
||||
import {dissoc, fromPairs, last, poll, randomId, tryCatch} from "@welshman/lib"
|
||||
import {AuthStatus, RelayReasonPrefix, SocketStatus, matchReason} from "@welshman/net"
|
||||
import type {Socket} from "@welshman/net"
|
||||
import {
|
||||
displayRelayUrl,
|
||||
|
|
@ -16,13 +16,12 @@ import {
|
|||
import {RelayJoin, RelayLeave, RoomJoin, RoomLeave} from "@welshman/domain"
|
||||
import {Sync, User, publish} from "@welshman/app"
|
||||
import {stripPrefix} from "@lib/util"
|
||||
import {app, command, relayManagement, roomLists, thunks, writer} from "@app/core"
|
||||
import {app, command, roomLists, thunks, writer} from "@app/core"
|
||||
import {PLATFORM_URL} from "@app/env"
|
||||
import {spaceManagement} from "@app/management"
|
||||
import {relaysMostlyRestricted} from "@app/policies"
|
||||
import {Push} from "@app/push"
|
||||
import {optInSpaceNotifications} from "@app/push"
|
||||
import {deriveSocket} from "@app/relays"
|
||||
import {notificationSettings, removeTrustedRelay, setSpaceNotifications} from "@app/settings"
|
||||
import {removeTrustedRelay} from "@app/settings"
|
||||
import {syncApplicationData} from "@app/sync"
|
||||
|
||||
export const ROOM_CREATE_INVITE = 9009
|
||||
|
|
@ -34,12 +33,6 @@ export type InviteData = {
|
|||
code?: string
|
||||
}
|
||||
|
||||
export const isNetworkAuthError = (error: string) => {
|
||||
const lower = error.toLowerCase()
|
||||
|
||||
return lower.includes("failed") || lower.includes("timeout") || lower.includes("network")
|
||||
}
|
||||
|
||||
export const parseInviteLink = (invite: string): InviteData | undefined => {
|
||||
if (invite.length < 3 || !invite.includes(".")) {
|
||||
return
|
||||
|
|
@ -83,7 +76,7 @@ export const makeInviteLink = (data: InviteData) => {
|
|||
}
|
||||
|
||||
export const shouldIgnoreError = (error: string) => {
|
||||
const isIgnored = error.startsWith("mute: ")
|
||||
const isIgnored = matchReason(RelayReasonPrefix.Mute, error)
|
||||
const isAborted = error.includes("Signing was aborted")
|
||||
const isStrictNip29Relay = error.includes("missing group (`h`) tag")
|
||||
|
||||
|
|
@ -249,177 +242,39 @@ export const attemptRelayAccess = async (url: string, claim = "") => {
|
|||
return stripPrefix(error)
|
||||
}
|
||||
|
||||
export class Access {
|
||||
url: string
|
||||
authError: Readable<string | undefined>
|
||||
loading = writable(true)
|
||||
claim = writable("")
|
||||
claimFailed = writable(false)
|
||||
roomCode = writable<string | undefined>()
|
||||
roomInviteError = writable<string | undefined>()
|
||||
isExplicitAuthError: Readable<boolean>
|
||||
isGenericError: Readable<boolean>
|
||||
|
||||
constructor(url: string) {
|
||||
this.url = url
|
||||
this.authError = deriveRelayAuthError(url)
|
||||
this.isExplicitAuthError = derived([this.authError], ([$authError]) =>
|
||||
Boolean($authError && !isNetworkAuthError($authError)),
|
||||
)
|
||||
this.isGenericError = derived([this.authError], ([$authError]) =>
|
||||
Boolean($authError && isNetworkAuthError($authError)),
|
||||
)
|
||||
}
|
||||
|
||||
createInviteStatus(requireCode: boolean) {
|
||||
return derived(
|
||||
[
|
||||
this.loading,
|
||||
this.isGenericError,
|
||||
this.isExplicitAuthError,
|
||||
this.roomInviteError,
|
||||
this.roomCode,
|
||||
this.claimFailed,
|
||||
export const completeSpaceJoin = async (url: string, notifications: boolean) => {
|
||||
await optInSpaceNotifications(url, notifications)
|
||||
await roomLists.get().addRelay(url).then(publish)
|
||||
relaysMostlyRestricted.update(dissoc(url))
|
||||
syncApplicationData()
|
||||
app
|
||||
.get()
|
||||
.use(Sync)
|
||||
.push({
|
||||
relays: [url],
|
||||
filters: [
|
||||
{
|
||||
kinds: [RELAYS, MESSAGING_RELAYS, FOLLOWS, PROFILE],
|
||||
authors: [User.require(app.get()).pubkey],
|
||||
},
|
||||
],
|
||||
([$loading, $isGeneric, $isExplicit, $roomInviteError, $roomCode, $claimFailed]) => {
|
||||
if ($loading) {
|
||||
return "loading" as const
|
||||
}
|
||||
if ($isGeneric) {
|
||||
return "network" as const
|
||||
}
|
||||
if ($isExplicit || $roomInviteError) {
|
||||
return "auth" as const
|
||||
}
|
||||
if (requireCode && !$roomCode) {
|
||||
return "failed" as const
|
||||
}
|
||||
if ($claimFailed) {
|
||||
return "noclaim" as const
|
||||
}
|
||||
|
||||
return "ready" as const
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
clearRestricted() {
|
||||
relaysMostlyRestricted.update(dissoc(this.url))
|
||||
}
|
||||
|
||||
async attempt(claim = "") {
|
||||
return attemptRelayAccess(this.url, claim)
|
||||
}
|
||||
|
||||
async configureNotifications(enabled: boolean) {
|
||||
if (enabled) {
|
||||
if (!notificationSettings.get().push) {
|
||||
await setSpaceNotifications(this.url, true)
|
||||
} else {
|
||||
const permissions = await Push.request()
|
||||
|
||||
if (permissions.startsWith("granted")) {
|
||||
await setSpaceNotifications(this.url, true)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
await setSpaceNotifications(this.url, false)
|
||||
}
|
||||
}
|
||||
|
||||
async completeJoin(notifications: boolean) {
|
||||
await this.configureNotifications(notifications)
|
||||
await roomLists.get().addRelay(this.url).then(publish)
|
||||
this.clearRestricted()
|
||||
syncApplicationData()
|
||||
app
|
||||
.get()
|
||||
.use(Sync)
|
||||
.push({
|
||||
relays: [this.url],
|
||||
filters: [
|
||||
{
|
||||
kinds: [RELAYS, MESSAGING_RELAYS, FOLLOWS, PROFILE],
|
||||
authors: [User.require(app.get()).pubkey],
|
||||
},
|
||||
],
|
||||
})
|
||||
}
|
||||
|
||||
async joinSpace({
|
||||
claim = "",
|
||||
notifications,
|
||||
alreadyJoined = false,
|
||||
}: {
|
||||
claim?: string
|
||||
notifications: boolean
|
||||
alreadyJoined?: boolean
|
||||
}) {
|
||||
if (!alreadyJoined) {
|
||||
const error = await this.attempt(claim)
|
||||
|
||||
if (error) {
|
||||
return error
|
||||
}
|
||||
|
||||
await this.completeJoin(notifications)
|
||||
}
|
||||
}
|
||||
|
||||
async acceptInvite(data: InviteData, notifications: boolean) {
|
||||
const spaceUrls = roomLists.get().urls(User.require(app.get()).pubkey).get()
|
||||
const error = await this.joinSpace({
|
||||
claim: data.claim,
|
||||
notifications,
|
||||
alreadyJoined: spaceUrls.includes(data.url),
|
||||
})
|
||||
}
|
||||
|
||||
export const acceptInvite = async (data: InviteData, notifications: boolean) => {
|
||||
const spaceUrls = roomLists.get().urls(User.require(app.get()).pubkey).get()
|
||||
|
||||
if (!spaceUrls.includes(data.url)) {
|
||||
const error = await attemptRelayAccess(data.url, data.claim)
|
||||
|
||||
if (error) {
|
||||
return error
|
||||
}
|
||||
|
||||
if (data.h && data.code) {
|
||||
return joinRoom(this.url, data.h, data.code)
|
||||
}
|
||||
await completeSpaceJoin(data.url, notifications)
|
||||
}
|
||||
|
||||
async prepareInvite(h?: string) {
|
||||
this.loading.set(true)
|
||||
|
||||
try {
|
||||
const management = relayManagement.get().forUrl(this.url)
|
||||
|
||||
const [methods, roomInviteResult] = await Promise.all([
|
||||
spaceManagement.get().loadSupportedMethods(this.url),
|
||||
h ? publishRoomInvite(this.url, h) : Promise.resolve({code: undefined, error: undefined}),
|
||||
// Keep the spinner up long enough that a fast relay doesn't make it flash
|
||||
sleep(300),
|
||||
])
|
||||
|
||||
// A relay reporting methods relay-wide can still come back "blocked" for this user.
|
||||
if (methods.includes("createclaim")) {
|
||||
const {result: claims} = await management.listClaims()
|
||||
|
||||
if (claims?.[0]) {
|
||||
this.claim.set(claims[0])
|
||||
} else {
|
||||
const claim = randomId()
|
||||
const {error} = await management.createClaim(claim)
|
||||
|
||||
if (error) {
|
||||
this.claimFailed.set(true)
|
||||
} else {
|
||||
this.claim.set(claim)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (h) {
|
||||
this.roomCode.set(roomInviteResult.code)
|
||||
this.roomInviteError.set(roomInviteResult.error)
|
||||
}
|
||||
} finally {
|
||||
this.loading.set(false)
|
||||
}
|
||||
if (data.h && data.code) {
|
||||
return joinRoom(data.url, data.h, data.code)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
<script lang="ts">
|
||||
import {onMount} from "svelte"
|
||||
import {Share} from "@capacitor/share"
|
||||
import {randomId, sleep} from "@welshman/lib"
|
||||
import LinkRound from "@assets/icons/link-round.svg?dataurl"
|
||||
import Upload from "@assets/icons/upload.svg?dataurl"
|
||||
import Copy from "@assets/icons/copy.svg?dataurl"
|
||||
|
|
@ -9,8 +10,10 @@
|
|||
import Button from "@lib/components/Button.svelte"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import QRCode from "@app/components/QRCode.svelte"
|
||||
import {Access, makeInviteLink} from "@app/access"
|
||||
import {deriveRelayAuthError, makeInviteLink, publishRoomInvite} from "@app/access"
|
||||
import {relayManagement} from "@app/core"
|
||||
import {PLATFORM_NAME} from "@app/env"
|
||||
import {spaceManagement} from "@app/management"
|
||||
import {clip} from "@app/toast"
|
||||
|
||||
type Props = {
|
||||
|
|
@ -20,10 +23,7 @@
|
|||
|
||||
const {url, h}: Props = $props()
|
||||
|
||||
const access = new Access(url)
|
||||
const {claim, loading, roomCode} = access
|
||||
const inviteStatus = access.createInviteStatus(Boolean(h))
|
||||
const invite = $derived(makeInviteLink({url, claim: $claim, h, code: $roomCode}))
|
||||
const authError = deriveRelayAuthError(url)
|
||||
|
||||
const copyInvite = () => clip(invite)
|
||||
|
||||
|
|
@ -35,8 +35,51 @@
|
|||
}
|
||||
}
|
||||
|
||||
const prepareInvite = async () => {
|
||||
try {
|
||||
const management = relayManagement.get().forUrl(url)
|
||||
|
||||
const [methods, roomInvite] = await Promise.all([
|
||||
spaceManagement.get().loadSupportedMethods(url),
|
||||
h ? publishRoomInvite(url, h) : Promise.resolve({code: undefined, error: undefined}),
|
||||
// Keep the spinner up long enough that a fast relay doesn't make it flash
|
||||
sleep(300),
|
||||
])
|
||||
|
||||
// A relay reporting methods relay-wide can still come back "blocked" for this user.
|
||||
if (methods.includes("createclaim")) {
|
||||
const {result: claims} = await management.listClaims()
|
||||
|
||||
if (claims?.[0]) {
|
||||
claim = claims[0]
|
||||
} else {
|
||||
const newClaim = randomId()
|
||||
const {error} = await management.createClaim(newClaim)
|
||||
|
||||
if (error) {
|
||||
claimFailed = true
|
||||
} else {
|
||||
claim = newClaim
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
roomCode = roomInvite.code
|
||||
roomInviteError = roomInvite.error
|
||||
} finally {
|
||||
loading = false
|
||||
}
|
||||
}
|
||||
|
||||
let loading = $state(true)
|
||||
let claim = $state("")
|
||||
let claimFailed = $state(false)
|
||||
let roomCode = $state<string>()
|
||||
let roomInviteError = $state<string>()
|
||||
let canShare = $state(false)
|
||||
|
||||
const invite = $derived(makeInviteLink({url, claim, h, code: roomCode}))
|
||||
|
||||
onMount(async () => {
|
||||
try {
|
||||
const {value} = await Share.canShare()
|
||||
|
|
@ -45,20 +88,20 @@
|
|||
canShare = false
|
||||
}
|
||||
|
||||
await access.prepareInvite(h)
|
||||
await prepareInvite()
|
||||
})
|
||||
</script>
|
||||
|
||||
<div>
|
||||
{#if $inviteStatus === "loading"}
|
||||
{#if loading}
|
||||
<p class="flex justify-center items-center">
|
||||
<Spinner loading={$loading}>Requesting an invite link...</Spinner>
|
||||
<Spinner {loading}>Requesting an invite link...</Spinner>
|
||||
</p>
|
||||
{:else if $inviteStatus === "network"}
|
||||
{:else if $authError && /failed|timeout|network/i.test($authError)}
|
||||
<p class="flex justify-center items-center">
|
||||
Unable to reach the space. Please check your connection and try again.
|
||||
</p>
|
||||
{:else if $inviteStatus === "auth"}
|
||||
{:else if $authError || roomInviteError}
|
||||
<p class="flex justify-center items-center">
|
||||
{#if h}
|
||||
Oops! It looks like you're not allowed to create invites for this room.
|
||||
|
|
@ -66,7 +109,7 @@
|
|||
Oops! It looks like you're not a member of this space.
|
||||
{/if}
|
||||
</p>
|
||||
{:else if $inviteStatus === "failed"}
|
||||
{:else if h && !roomCode}
|
||||
<p class="flex justify-center items-center">Unable to create a room invite code.</p>
|
||||
{:else}
|
||||
<div class="flex flex-col items-center gap-6">
|
||||
|
|
@ -101,10 +144,10 @@
|
|||
{:else}
|
||||
This invite link can be used by clicking "Add Space" and pasting it there.
|
||||
{/if}
|
||||
{#if $inviteStatus === "noclaim"}
|
||||
{#if claimFailed}
|
||||
We weren't able to get an invite code from this space, so additional steps might be
|
||||
required.
|
||||
{:else if !$claim}
|
||||
{:else if !claim}
|
||||
This space did not issue a claim for this link, so additional steps might be required.
|
||||
{/if}
|
||||
</p>
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
import ModalSubtitle from "@lib/components/ModalSubtitle.svelte"
|
||||
import ModalFooter from "@lib/components/ModalFooter.svelte"
|
||||
import {pushToast} from "@app/toast"
|
||||
import {Access, parseInviteLink} from "@app/access"
|
||||
import {attemptRelayAccess, parseInviteLink} from "@app/access"
|
||||
|
||||
type Props = {
|
||||
url: string
|
||||
|
|
@ -24,8 +24,6 @@
|
|||
|
||||
const {url, callback}: Props = $props()
|
||||
|
||||
const access = new Access(url)
|
||||
|
||||
const back = () => history.back()
|
||||
|
||||
const join = async () => {
|
||||
|
|
@ -33,7 +31,7 @@
|
|||
|
||||
try {
|
||||
const claim = parseInviteLink(value)?.claim || value
|
||||
const message = await access.attempt(claim)
|
||||
const message = await attemptRelayAccess(url, claim)
|
||||
|
||||
if (message) {
|
||||
return pushToast({theme: "error", message, timeout: 30_000})
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@
|
|||
import SpaceJoinStatus from "@app/components/SpaceJoinStatus.svelte"
|
||||
import {pushToast} from "@app/toast"
|
||||
import {goToSpace, makeRoomPath} from "@app/routes"
|
||||
import {Access, parseInviteLink} from "@app/access"
|
||||
import {acceptInvite, parseInviteLink} from "@app/access"
|
||||
|
||||
type Props = {
|
||||
invite: string
|
||||
|
|
@ -45,9 +45,7 @@
|
|||
|
||||
const joinRelay = async () => {
|
||||
const data = inviteData!
|
||||
const access = new Access(data.url)
|
||||
|
||||
error = await access.acceptInvite(data, notifications)
|
||||
error = await acceptInvite(data, notifications)
|
||||
|
||||
if (error) {
|
||||
return
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
import SpaceAccessRequest from "@app/components/SpaceAccessRequest.svelte"
|
||||
import SpaceJoinNotifications from "@app/components/SpaceJoinNotifications.svelte"
|
||||
import SpaceJoinStatus from "@app/components/SpaceJoinStatus.svelte"
|
||||
import {Access} from "@app/access"
|
||||
import {attemptRelayAccess, completeSpaceJoin} from "@app/access"
|
||||
import {pushModal} from "@app/modal"
|
||||
import {pushToast} from "@app/toast"
|
||||
import {goToSpace} from "@app/routes"
|
||||
|
|
@ -25,8 +25,6 @@
|
|||
|
||||
const {url}: Props = $props()
|
||||
|
||||
const access = new Access(url)
|
||||
|
||||
const back = () => history.back()
|
||||
|
||||
const join = async () => {
|
||||
|
|
@ -37,7 +35,7 @@
|
|||
loading = true
|
||||
|
||||
try {
|
||||
await access.completeJoin(notifications)
|
||||
await completeSpaceJoin(url, notifications)
|
||||
|
||||
pushToast({message: "Welcome to the space!"})
|
||||
await goToSpace(url, {replaceState: true})
|
||||
|
|
@ -54,7 +52,7 @@
|
|||
let notifications = $state(true)
|
||||
|
||||
onMount(async () => {
|
||||
error = await access.attempt()
|
||||
error = await attemptRelayAccess(url)
|
||||
loading = false
|
||||
})
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import {Capacitor} from "@capacitor/core"
|
||||
import {notificationSettings} from "@app/settings"
|
||||
import {notificationSettings, setSpaceNotifications} from "@app/settings"
|
||||
import {pushState} from "@app/push/adapters/common"
|
||||
import {ElectronNotifications} from "@app/push/adapters/electron"
|
||||
import {WebNotifications} from "@app/push/adapters/web"
|
||||
|
|
@ -64,3 +64,15 @@ export class Push {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
export const optInSpaceNotifications = async (url: string, notify: boolean) => {
|
||||
if (notify && notificationSettings.get().push) {
|
||||
const permission = await Push.request()
|
||||
|
||||
if (permission.startsWith("granted")) {
|
||||
await setSpaceNotifications(url, true)
|
||||
}
|
||||
} else {
|
||||
await setSpaceNotifications(url, notify)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue