Something went wrong. Try again.
Client side atproto account migrator in your web browser, along with services for backups and adversarial migrations. pdsmoover.com
pds atproto migrations moo cow
Something went wrong. Try again.
4.3 kB · 114 lines
Svelte
at oauth
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115<script lang="ts"> import {untrack} from 'svelte'; import {deactivateOldAccount, isAuthFactorError} from '$lib/moover'; import Button from '$lib/components/ui/Button.svelte'; import ErrorMessage from '$lib/components/ui/ErrorMessage.svelte'; import HandleTypeaheadField from '$lib/components/ui/HandleTypeaheadField.svelte'; import InputField from '$lib/components/ui/InputField.svelte'; import StatusMessage from '$lib/components/ui/StatusMessage.svelte';
interface Props { /** Prefill for the old handle/DID; a migration already knows the account's DID. */ initialHandle?: string; /** Called after the old account was successfully deactivated. */ onDeactivated?: () => void; /** Mirrors the progress text so a parent can show its own status line too. */ onStatus?: (status: string) => void; /** Submit button label. */ submitLabel?: string; }
let { initialHandle = '', onDeactivated, onStatus, submitLabel = 'Turn it off', }: Props = $props();
// The migration prefills the old account's DID; the user can still edit it. untrack makes the // one-time read of the prop explicit, so the compiler does not warn about capturing it. let oldHandle = $state(untrack(() => initialHandle)); let oldPassword = $state(''); let twoFactorCode = $state(''); let showTwoFactorCodeInput = $state(false); let errorMessage: string | null = $state(null); let statusMessage = $state(''); let isSubmitting = $state(false); let done = $state(false);
function updateStatusHandler(status: string) { statusMessage = status; onStatus?.(status); }
async function handleSubmit(event: SubmitEvent) { event.preventDefault(); errorMessage = null; isSubmitting = true;
try { if (showTwoFactorCodeInput && !twoFactorCode) { errorMessage = 'Please enter the 2FA that was sent to your email.'; return; }
await deactivateOldAccount(oldHandle, oldPassword, updateStatusHandler, twoFactorCode); done = true; onDeactivated?.(); } catch (error) { if (isAuthFactorError(error)) { showTwoFactorCodeInput = true; } errorMessage = error instanceof Error ? error.message : 'Could not turn off the old account'; // The last progress line names a step that is no longer running. updateStatusHandler(''); } finally { isSubmitting = false; } }
function reset() { oldPassword = ''; twoFactorCode = ''; showTwoFactorCodeInput = false; errorMessage = null; statusMessage = ''; done = false; }</script>
{#if done} <div class="mt-7.5 text-left"> <StatusMessage>Your old account is deactivated.</StatusMessage> <div class="mt-3"> <Button variant="secondary" onclick={reset}>Turn off another account</Button> </div> </div>{:else} <form id="turn-off-form" onsubmit={handleSubmit}> <!-- Login credentials --> <div class="mt-7.5 text-left"> <h2>Login for your old PDS</h2> <HandleTypeaheadField id="turn-off-handle" label="Old Handle (or DID):" placeholder="alice.bsky.social" autocomplete="username" required bind:value={oldHandle}/> <InputField id="turn-off-password" label="Old Password:" type="password" autocomplete="current-password" required bind:value={oldPassword}/>
{#if showTwoFactorCodeInput} <InputField id="turn-off-2fa" label="2FA from the email sent" autocomplete="one-time-code" bind:value={twoFactorCode}/> {/if} </div>
{#if errorMessage} <ErrorMessage>{errorMessage}</ErrorMessage> {/if} {#if statusMessage} <StatusMessage spinner={isSubmitting}>{statusMessage}</StatusMessage> {/if}
<div class="mb-3.75 text-left"> <Button type="submit" busy={isSubmitting}>{submitLabel}</Button> </div> </form>{/if}