feat: add deep link customization
This commit is contained in:
@@ -0,0 +1,201 @@
|
||||
<script lang="ts">
|
||||
import {onMount} from "svelte"
|
||||
import {get} from "svelte/store"
|
||||
import {goto} from "$app/navigation"
|
||||
import {
|
||||
userFollowList,
|
||||
userBlossomServerList,
|
||||
tagPubkey,
|
||||
publishThunk,
|
||||
addRelay,
|
||||
pubkey,
|
||||
profilesByPubkey,
|
||||
} from "@welshman/app"
|
||||
import {
|
||||
makeEvent,
|
||||
FOLLOWS,
|
||||
BLOSSOM_SERVERS,
|
||||
getListTags,
|
||||
getPubkeyTagValues,
|
||||
getTagValues,
|
||||
tagger,
|
||||
RelayMode,
|
||||
makeProfile,
|
||||
} from "@welshman/util"
|
||||
import {Router} from "@welshman/router"
|
||||
import ModalHeader from "@lib/components/ModalHeader.svelte"
|
||||
import ModalFooter from "@lib/components/ModalFooter.svelte"
|
||||
import ModalBody from "@lib/components/ModalBody.svelte"
|
||||
import ModalTitle from "@lib/components/ModalTitle.svelte"
|
||||
import ModalSubtitle from "@lib/components/ModalSubtitle.svelte"
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
import AltArrowLeft from "@assets/icons/alt-arrow-left.svg?dataurl"
|
||||
import {theme} from "@app/util/theme"
|
||||
import {pushModal} from "@app/util/modal"
|
||||
import {pushToast} from "@app/util/toast"
|
||||
import ThreadCreate from "@app/components/ThreadCreate.svelte"
|
||||
import {addSpaceMembership, updateProfile} from "@app/core/commands"
|
||||
|
||||
type Props = {
|
||||
params: URLSearchParams
|
||||
back: () => void
|
||||
}
|
||||
|
||||
const {params, back}: Props = $props()
|
||||
|
||||
const t = params.get("theme")
|
||||
const relays = params.getAll("add_relay")
|
||||
const blossoms = params.getAll("add_blossom")
|
||||
const follows = params.getAll("add_follow")
|
||||
const joins = params.getAll("join")
|
||||
|
||||
const profile_name = params.get("profile_name")
|
||||
const profile_about = params.get("profile_about")
|
||||
const profile_picture = params.get("profile_picture")
|
||||
const profile_banner = params.get("profile_banner")
|
||||
|
||||
const shareRelay = params.get("share_relay") || params.get("share_url")
|
||||
const shareH = params.get("share_h")
|
||||
const shareText = params.get("share_text")
|
||||
|
||||
const hasProfile = !!profile_name || !!profile_about || !!profile_picture || !!profile_banner
|
||||
const hasSettings =
|
||||
!!t ||
|
||||
relays.length > 0 ||
|
||||
blossoms.length > 0 ||
|
||||
follows.length > 0 ||
|
||||
joins.length > 0 ||
|
||||
hasProfile
|
||||
const hasShare = !!shareRelay && !!shareH
|
||||
|
||||
let processing = $state(false)
|
||||
|
||||
onMount(() => {
|
||||
if (!hasSettings) {
|
||||
if (hasShare) {
|
||||
doShare()
|
||||
} else {
|
||||
pushToast({message: "No valid intent actions found", theme: "error"})
|
||||
back()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const doShare = () => {
|
||||
back()
|
||||
goto(`/spaces/${encodeURIComponent(shareRelay!)}/${shareH}`).then(() => {
|
||||
pushModal(ThreadCreate, {url: shareRelay!, h: shareH!, initialContent: shareText || ""})
|
||||
})
|
||||
}
|
||||
|
||||
const accept = async () => {
|
||||
processing = true
|
||||
|
||||
if (t === "dark" || t === "light" || t === "system") {
|
||||
theme.set(t)
|
||||
}
|
||||
|
||||
if (relays.length > 0) {
|
||||
for (const url of relays) {
|
||||
addRelay(url, RelayMode.Read)
|
||||
addRelay(url, RelayMode.Write)
|
||||
}
|
||||
}
|
||||
|
||||
if (blossoms.length > 0) {
|
||||
const current = getTagValues("server", getListTags(get(userBlossomServerList)))
|
||||
const updated = Array.from(new Set([...current, ...blossoms]))
|
||||
publishThunk({
|
||||
event: makeEvent(BLOSSOM_SERVERS, {tags: updated.map(tagger("server"))}),
|
||||
relays: Router.get().FromUser().getUrls(),
|
||||
})
|
||||
}
|
||||
|
||||
if (follows.length > 0) {
|
||||
const current = getPubkeyTagValues(getListTags(get(userFollowList)))
|
||||
const updated = Array.from(new Set([...current, ...follows]))
|
||||
publishThunk({
|
||||
event: makeEvent(FOLLOWS, {tags: updated.map(tagPubkey)}),
|
||||
relays: Router.get().FromUser().getUrls(),
|
||||
})
|
||||
}
|
||||
|
||||
if (joins.length > 0) {
|
||||
for (const url of joins) {
|
||||
await addSpaceMembership(url)
|
||||
}
|
||||
}
|
||||
|
||||
if (hasProfile) {
|
||||
const p = get(profilesByPubkey).get(get(pubkey)!) || makeProfile()
|
||||
if (profile_name) p.name = profile_name
|
||||
if (profile_about) p.about = profile_about
|
||||
if (profile_picture) p.picture = profile_picture
|
||||
if (profile_banner) p.banner = profile_banner
|
||||
// assuming shouldBroadcast makes it public
|
||||
await updateProfile({profile: p, shouldBroadcast: true})
|
||||
}
|
||||
|
||||
pushToast({message: "Customizations Applied!"})
|
||||
|
||||
if (hasShare) {
|
||||
doShare()
|
||||
} else {
|
||||
back()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if hasSettings}
|
||||
<form class="flex flex-col gap-4 text-center">
|
||||
<ModalHeader>
|
||||
<div class="flex items-center gap-2">
|
||||
<Button class="btn btn-neutral btn-sm" onclick={back}>
|
||||
<Icon icon={AltArrowLeft} />
|
||||
</Button>
|
||||
<div class="flex flex-col items-start gap-1">
|
||||
<ModalTitle>Apply Customization?</ModalTitle>
|
||||
<ModalSubtitle>A link is requesting to customize your app.</ModalSubtitle>
|
||||
</div>
|
||||
</div>
|
||||
</ModalHeader>
|
||||
|
||||
<ModalBody>
|
||||
<p>This link will apply the following changes:</p>
|
||||
<ul class="text-left list-disc list-inside px-6 py-4">
|
||||
{#if t}
|
||||
<li>Set theme to "{t}"</li>
|
||||
{/if}
|
||||
{#if relays.length > 0}
|
||||
<li>Add {relays.length} relay{relays.length > 1 ? "s" : ""} to your settings</li>
|
||||
{/if}
|
||||
{#if blossoms.length > 0}
|
||||
<li>Add {blossoms.length} blossom server{blossoms.length > 1 ? "s" : ""}</li>
|
||||
{/if}
|
||||
{#if follows.length > 0}
|
||||
<li>Follow {follows.length} person{follows.length > 1 ? "s" : ""}</li>
|
||||
{/if}
|
||||
{#if joins.length > 0}
|
||||
<li>Join {joins.length} communit{joins.length > 1 ? "ies" : "y"}</li>
|
||||
{/if}
|
||||
{#if hasProfile}
|
||||
<li>Update your profile metadata</li>
|
||||
{/if}
|
||||
{#if hasShare}
|
||||
<li>Open a new post dialog in a specific room</li>
|
||||
{/if}
|
||||
</ul>
|
||||
</ModalBody>
|
||||
|
||||
<ModalFooter>
|
||||
<Button class="btn btn-neutral" onclick={back}>Cancel</Button>
|
||||
<Button class="btn btn-primary" onclick={accept} disabled={processing}>
|
||||
{#if processing}
|
||||
<span class="loading loading-spinner"></span>
|
||||
{/if}
|
||||
Accept
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</form>
|
||||
{/if}
|
||||
@@ -23,9 +23,10 @@
|
||||
type Props = {
|
||||
url: string
|
||||
h?: string
|
||||
initialContent?: string
|
||||
}
|
||||
|
||||
const {url, h}: Props = $props()
|
||||
const {url, h, initialContent = ""}: Props = $props()
|
||||
|
||||
const shouldProtect = canEnforceNip70(url)
|
||||
|
||||
@@ -73,7 +74,13 @@
|
||||
history.back()
|
||||
}
|
||||
|
||||
const editor = makeEditor({url, submit, uploading, placeholder: "What's on your mind?"})
|
||||
const editor = makeEditor({
|
||||
content: initialContent,
|
||||
url,
|
||||
submit,
|
||||
uploading,
|
||||
placeholder: "What's on your mind?",
|
||||
})
|
||||
|
||||
let title: string = $state("")
|
||||
</script>
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
<script lang="ts">
|
||||
import {page} from "$app/stores"
|
||||
import {goto} from "$app/navigation"
|
||||
import Dialog from "@lib/components/Dialog.svelte"
|
||||
import IntentHandler from "@app/components/IntentHandler.svelte"
|
||||
|
||||
const children = {
|
||||
component: IntentHandler,
|
||||
props: {
|
||||
params: $page.url.searchParams,
|
||||
back: () => goto("/home"),
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<Dialog {children} />
|
||||
Reference in New Issue
Block a user