import { CheckIcon, CopyIcon, PlusIcon } from 'lucide-react';
import { useState } from 'react';
import { Button } from '#/components/atoms/button.jsx';
import { Notice } from '#/components/atoms/notice.jsx';
import { Dialog } from '#/components/molecules/dialog.jsx';
import { SelectField } from '#/components/molecules/select-field.jsx';
import { TextField } from '#/components/molecules/text-field.jsx';
import { xrpcPost } from '#/lib/api.js';
import { queryKeys, useApiMutation } from '#/lib/query.js';
/** The shell commands GitHub shows after creating a repository, our spelling. */
function pushInstructions(cloneUrl) {
return [`git remote add origin ${cloneUrl}`, 'git push -u origin main'].join(
'\n',
);
}
export function CommandBlock({ text }) {
const [copied, setCopied] = useState(false);
const copy = async () => {
await navigator.clipboard.writeText(text);
setCopied(true);
setTimeout(() => setCopied(false), 1500);
};
return (
{text}
);
}
/**
* The push commands for a repository nothing has been pushed to. The dialog
* shows these right after creating, and the repository page keeps showing
* them until the first push, so closing the dialog loses nothing.
* @param {{name: string, cloneUrl: string, authority: string, isPrivate: boolean}} props
*/
export function SetupInstructions({ name, cloneUrl, authority, isPrivate }) {
return (
{isPrivate && (
This repository is private. Space members you add can read it; nobody
else can.
)}
Nothing has been pushed yet. From an existing local repository:
Starting from nothing:
Pushing needs the helper: npm install -g @pdsjs/git, then
git-remote-atproto login {authority}.
);
}
/**
* The dialog behind the New repository button. Creating writes an empty
* repository record at once, private ones into their space, so the list and
* the repository page exist before the first push.
* @param {{data: {handle?: string|null, did: string, features: Record}, onNavigate: (href: string) => void}} props
*/
export function NewRepoDialog({ data, onNavigate }) {
const authority = data.handle || data.did;
const canPrivate = Boolean(data.features.spaces && data.features.spaceAdmin);
const [name, setName] = useState('');
const [visibility, setVisibility] = useState('public');
/** @type {[{cloneUrl: string, space: string|null}|null, Function]} */
const [created, setCreated] = useState(null);
const action = useApiMutation([
queryKeys.spaces,
queryKeys.gitRepos,
queryKeys.overview,
]);
const validName = /^[A-Za-z0-9._~-]{1,512}$/.test(name);
const submit = async (event) => {
event.preventDefault();
if (!validName) return;
await action.run(async () => {
const payload =
visibility === 'private' ? { name, private: true } : { name };
const { space } = await xrpcPost('dev.pdsjs.git.createRepo', payload);
// Public or private, the address is the same: the helper finds the
// space a private repository keeps under its own name.
setCreated({ space, cloneUrl: `atproto://${authority}/${name}` });
});
};
const repoHref = created
? `/account/git/${encodeURIComponent(name)}${created.space ? `?space=${encodeURIComponent(created.space)}` : ''}`
: '';
return (
);
}