mirror of
https://github.com/Blah-IM/Weblah.git
synced 2025-04-30 16:21:09 +00:00
feat: domain setup
This commit is contained in:
parent
b3e16f2063
commit
833460d709
5 changed files with 75 additions and 30 deletions
21
src/lib/components/Card.svelte
Normal file
21
src/lib/components/Card.svelte
Normal file
|
@ -0,0 +1,21 @@
|
|||
<script lang="ts">
|
||||
import { tw } from '$lib/tw';
|
||||
import type { HTMLAttributes } from 'svelte/elements';
|
||||
|
||||
interface Props extends HTMLAttributes<HTMLDivElement> {
|
||||
class?: string;
|
||||
children?: import('svelte').Snippet;
|
||||
}
|
||||
|
||||
let { children, class: classNames, ...rest }: Props = $props();
|
||||
</script>
|
||||
|
||||
<div
|
||||
class={tw(
|
||||
'border-ss-secondary bg-sb-primary overflow-hidden rounded-lg border-[0.5px] shadow-xs',
|
||||
classNames
|
||||
)}
|
||||
{...rest}
|
||||
>
|
||||
{@render children?.()}
|
||||
</div>
|
|
@ -1,4 +1,6 @@
|
|||
<script lang="ts">
|
||||
import Card from '../Card.svelte';
|
||||
|
||||
interface Props {
|
||||
header?: import('svelte').Snippet | string;
|
||||
children?: import('svelte').Snippet;
|
||||
|
@ -18,11 +20,9 @@
|
|||
{/if}
|
||||
</h3>
|
||||
{/if}
|
||||
<div
|
||||
class="divide-ss-secondary border-ss-secondary bg-sb-primary divide-y-[0.5px] overflow-hidden rounded-lg border-[0.5px] shadow-xs"
|
||||
>
|
||||
<Card class="divide-ss-secondary divide-y-[0.5px]">
|
||||
{@render children?.()}
|
||||
</div>
|
||||
</Card>
|
||||
{#if footer}
|
||||
<div class="text-sf-tertiary mt-1 px-4 text-sm">
|
||||
{#if typeof footer === 'string'}
|
||||
|
|
|
@ -9,6 +9,8 @@ export function idURLToUsername(idURL: string): string {
|
|||
return url.host;
|
||||
}
|
||||
|
||||
export const identityDescriptionFilePath = '/.well-known/blah/identity.json';
|
||||
|
||||
export type IDURLValidity =
|
||||
| { valid: true }
|
||||
| ({ valid: false } & (
|
||||
|
@ -39,7 +41,7 @@ export async function validateIDURL(url: string, identity: BlahIdentity): Promis
|
|||
|
||||
const profileFileURL = (() => {
|
||||
let url = idURL;
|
||||
url.pathname = '/.well-known/blah/identity.json';
|
||||
url.pathname = identityDescriptionFilePath;
|
||||
return url.toString();
|
||||
})();
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
<script lang="ts">
|
||||
import Dialog from '$lib/components/Dialog.svelte';
|
||||
import { GroupedListItem } from '$lib/components/GroupedList';
|
||||
import { idURLToUsername, validateIDURL, type IDURLValidity } from '$lib/idURL';
|
||||
import type { BlahIdentity } from '@blah-im/core/identity';
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
<script lang="ts">
|
||||
import Button from '$lib/components/Button.svelte';
|
||||
import Card from '$lib/components/Card.svelte';
|
||||
import Dialog from '$lib/components/Dialog.svelte';
|
||||
import { GroupedListContainer, GroupedListSection } from '$lib/components/GroupedList';
|
||||
import GroupedListContent from '$lib/components/GroupedList/GroupedListContent.svelte';
|
||||
import Link from '$lib/components/Link.svelte';
|
||||
import PageHeader from '$lib/components/PageHeader.svelte';
|
||||
import { idURLToUsername } from '$lib/idURL';
|
||||
import { identityDescriptionFilePath, idURLToUsername } from '$lib/idURL';
|
||||
import type { BlahIdentity } from '@blah-im/core/identity';
|
||||
|
||||
interface Props {
|
||||
|
@ -19,6 +19,26 @@
|
|||
const profileDescriptionString = $derived(
|
||||
JSON.stringify(identity.generateIdentityDescription(), null, 2)
|
||||
);
|
||||
|
||||
let copied = $state(false);
|
||||
let jsonFileBlobHref: string | null = $state(null);
|
||||
|
||||
const copyToClipboard = async () => {
|
||||
await navigator.clipboard.writeText(profileDescriptionString);
|
||||
copied = true;
|
||||
setTimeout(() => (copied = false), 2000);
|
||||
};
|
||||
|
||||
$effect.pre(() => {
|
||||
if (identity) {
|
||||
const blob = new Blob([profileDescriptionString], { type: 'application/json' });
|
||||
jsonFileBlobHref = URL.createObjectURL(blob);
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (jsonFileBlobHref) URL.revokeObjectURL(jsonFileBlobHref);
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
||||
<Dialog bind:open class="flex h-2/3 flex-col">
|
||||
|
@ -27,25 +47,28 @@
|
|||
<Button variant="primary" onclick={() => (open = false)}>Done</Button>
|
||||
</PageHeader>
|
||||
|
||||
<GroupedListContainer class="w-full grow overflow-x-auto">
|
||||
<GroupedListSection>
|
||||
{#snippet header()}
|
||||
<div class="-me-4 flex min-w-0 items-end gap-2 text-base normal-case">
|
||||
<p class="text-sf-primary">
|
||||
For others to validate your domain as your username, put the content below at
|
||||
<code>/.well-known/blah/profile.json</code>
|
||||
under your domain.
|
||||
</p>
|
||||
<Button>Copy</Button>
|
||||
</div>
|
||||
{/snippet}
|
||||
<GroupedListContent class="p-0">
|
||||
<textarea
|
||||
readonly
|
||||
class="text-sf-primary block h-100 w-full resize-none overflow-x-auto px-4 py-3 font-mono"
|
||||
value={profileDescriptionString}
|
||||
></textarea>
|
||||
</GroupedListContent>
|
||||
</GroupedListSection>
|
||||
</GroupedListContainer>
|
||||
<div class="flex grow flex-col gap-3 p-3">
|
||||
<p class="text-sf-primary px-4 text-sm">
|
||||
For others to validate ownership of your domain, make text file below available as
|
||||
<code>{identityDescriptionFilePath}</code>
|
||||
under your domain, and make sure it allows any cross domain requests.
|
||||
<Link href="/">Learn more...</Link>
|
||||
</p>
|
||||
<Card class="relative grow">
|
||||
<textarea
|
||||
readonly
|
||||
class="text-sf-primary block h-full w-full resize-none overflow-x-auto px-4 py-3 font-mono text-sm"
|
||||
value={profileDescriptionString}
|
||||
></textarea>
|
||||
|
||||
<div class="absolute end-2 top-2">
|
||||
<Button onclick={copyToClipboard}>
|
||||
{#if copied}Copied!{:else}Copy{/if}
|
||||
</Button>
|
||||
{#if jsonFileBlobHref}
|
||||
<Button href={jsonFileBlobHref} download="identity.json">Download</Button>
|
||||
{/if}
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
</Dialog>
|
||||
|
|
Loading…
Add table
Reference in a new issue