A calm place to write long-form, and publish it to the open social web.
skypress.blog/
1import { useState } from 'react';
2
3interface Props {
4 /** True when opened from "Publish" — the copy promises a publish on return. */
5 forPublish: boolean;
6 error: string | null;
7 onSubmit: ( value: string ) => void;
8 onCancel?: () => void;
9}
10
11/**
12 * Signed-out handle entry for the writing-first flow. OAuth is sign-in only, so this never
13 * creates an account — it links out to Bluesky's hosted signup instead (brief guardrail). The
14 * caller persists the draft + sets publish intent before the redirect.
15 */
16export default function SignInPanel( { forPublish, error, onSubmit, onCancel }: Props ) {
17 const [ value, setValue ] = useState( '' );
18
19 return (
20 <form
21 className="signin-panel"
22 onSubmit={ ( event ) => {
23 event.preventDefault();
24 onSubmit( value.trim() );
25 } }
26 >
27 <h2 className="signin-panel__title">
28 { forPublish ? 'Sign in to publish' : 'Sign in' }
29 </h2>
30 <p className="signin-panel__lede">
31 { forPublish
32 ? "Your draft is saved. Sign in and we'll pick up right where you left off and publish it."
33 : 'Use your existing Bluesky / AT Protocol identity. Your work stays in your own account.' }
34 </p>
35 <label className="signin-panel__label" htmlFor="write-handle">
36 Your handle, DID, or PDS URL
37 </label>
38 <input
39 id="write-handle"
40 className="signin-panel__input"
41 name="handle"
42 autoComplete="username"
43 autoCapitalize="none"
44 autoCorrect="off"
45 spellCheck={ false }
46 placeholder="alice.bsky.social"
47 value={ value }
48 onChange={ ( event ) => setValue( event.target.value ) }
49 />
50 <div className="signin-panel__actions">
51 <button className="signin-panel__submit" type="submit">
52 Sign in with AT Protocol
53 </button>
54 { onCancel && (
55 <button className="signin-panel__cancel" type="button" onClick={ onCancel }>
56 Cancel
57 </button>
58 ) }
59 </div>
60 { error && (
61 <p className="signin-panel__error" role="alert">
62 { error }
63 </p>
64 ) }
65 <p className="signin-panel__signup">
66 Need an account?{ ' ' }
67 <a
68 className="signin-panel__signup-link"
69 href="https://bsky.app"
70 target="_blank"
71 rel="noopener noreferrer"
72 aria-label="Create an account on Bluesky (opens in a new tab)"
73 >
74 Create one on Bluesky
75 { /* Standard external-link glyph — same icon as the author page's Bluesky link. */ }
76 <svg
77 className="signin-panel__external"
78 width="11"
79 height="11"
80 viewBox="0 0 24 24"
81 fill="none"
82 stroke="currentColor"
83 strokeWidth="2.5"
84 strokeLinecap="round"
85 strokeLinejoin="round"
86 aria-hidden="true"
87 >
88 <path d="M7 17 17 7" />
89 <path d="M8 7h9v9" />
90 </svg>
91 </a>
92 </p>
93 </form>
94 );
95}