Highly ambitious ATProtocol AppView service and sdks

use checkboxes instead of inputs for sync modal

Changed files
+77 -41
frontend
src
features
slices
sync
templates
fragments
shared
fragments
+2 -13
frontend/src/features/slices/sync/handlers.tsx
··· 34 34 35 35 try { 36 36 const formData = await req.formData(); 37 - const collectionsText = (formData.get("collections") as string) || ""; 38 - const externalCollectionsText = 39 - (formData.get("external_collections") as string) || ""; 37 + const collections = formData.getAll("collections") as string[]; 38 + const externalCollections = formData.getAll("external_collections") as string[]; 40 39 const reposText = (formData.get("repos") as string) || ""; 41 - 42 - const collections = collectionsText 43 - .split("\n") 44 - .map((line) => line.trim()) 45 - .filter((line) => line.length > 0); 46 - 47 - const externalCollections = externalCollectionsText 48 - .split("\n") 49 - .map((line) => line.trim()) 50 - .filter((line) => line.length > 0); 51 40 52 41 const repos = reposText 53 42 .split("\n")
+47 -28
frontend/src/features/slices/sync/templates/fragments/SyncFormModal.tsx
··· 1 1 import { Button } from "../../../../../shared/fragments/Button.tsx"; 2 2 import { Textarea } from "../../../../../shared/fragments/Textarea.tsx"; 3 + import { Checkbox } from "../../../../../shared/fragments/Checkbox.tsx"; 3 4 import { Modal } from "../../../../../shared/fragments/Modal.tsx"; 4 5 5 6 interface SyncFormModalProps { ··· 24 25 hx-swap="innerHTML" 25 26 className="space-y-4" 26 27 > 27 - <Textarea 28 - id="collections" 29 - name="collections" 30 - label="Primary Collections" 31 - rows={4} 32 - placeholder={collections.length > 0 33 - ? "Primary collections (matching your slice domain) loaded below:" 34 - : "Enter primary collections matching your slice domain, one per line:\n\nyour.domain.collection\nyour.domain.post"} 35 - defaultValue={collections.length > 0 ? collections.join("\n") : ""} 36 - /> 37 - <p className="mt-1 text-xs text-zinc-500"> 38 - Primary collections are those that match your slice's domain. 39 - </p> 28 + <div className="space-y-2"> 29 + <label className="block text-sm font-medium text-zinc-700 dark:text-zinc-300"> 30 + Primary Collections 31 + </label> 32 + <p className="text-xs text-zinc-500"> 33 + Primary collections are those that match your slice's domain. 34 + </p> 35 + {collections.length > 0 ? ( 36 + <div className="space-y-2 border border-zinc-200 dark:border-zinc-700 rounded-md p-4 bg-white dark:bg-zinc-900"> 37 + {collections.map((collection) => ( 38 + <Checkbox 39 + key={collection} 40 + name="collections" 41 + value={collection} 42 + label={collection} 43 + checked 44 + /> 45 + ))} 46 + </div> 47 + ) : ( 48 + <p className="text-sm text-zinc-500 italic">No primary collections available</p> 49 + )} 50 + </div> 40 51 41 - <Textarea 42 - id="external_collections" 43 - name="external_collections" 44 - label="External Collections" 45 - rows={4} 46 - placeholder={externalCollections.length > 0 47 - ? "External collections loaded below:" 48 - : "Enter external collections (not matching your domain), one per line:\n\napp.bsky.feed.post\napp.bsky.actor.profile"} 49 - defaultValue={externalCollections.length > 0 50 - ? externalCollections.join("\n") 51 - : ""} 52 - /> 53 - <p className="mt-1 text-xs text-zinc-500"> 54 - External collections are those that don't match your slice's domain. 55 - </p> 52 + <div className="space-y-2"> 53 + <label className="block text-sm font-medium text-zinc-700 dark:text-zinc-300"> 54 + External Collections 55 + </label> 56 + <p className="text-xs text-zinc-500"> 57 + External collections are those that don't match your slice's domain. 58 + </p> 59 + {externalCollections.length > 0 ? ( 60 + <div className="space-y-2 border border-zinc-200 dark:border-zinc-700 rounded-md p-4 bg-white dark:bg-zinc-900"> 61 + {externalCollections.map((collection) => ( 62 + <Checkbox 63 + key={collection} 64 + name="external_collections" 65 + value={collection} 66 + label={collection} 67 + checked 68 + /> 69 + ))} 70 + </div> 71 + ) : ( 72 + <p className="text-sm text-zinc-500 italic">No external collections available</p> 73 + )} 74 + </div> 56 75 57 76 <Textarea 58 77 id="repos"
+28
frontend/src/shared/fragments/Checkbox.tsx
··· 1 + import type { JSX } from "preact"; 2 + import { cn } from "../../utils/cn.ts"; 3 + 4 + export type CheckboxProps = JSX.IntrinsicElements["input"] & { 5 + label?: string; 6 + }; 7 + 8 + export function Checkbox(props: CheckboxProps): JSX.Element { 9 + const { class: classProp, label, ...rest } = props; 10 + 11 + const className = cn("accent-blue-600 dark:accent-white", classProp); 12 + 13 + return ( 14 + <label className="flex items-center gap-2 cursor-pointer"> 15 + <input 16 + type="checkbox" 17 + class={className} 18 + style="color-scheme: light dark;" 19 + {...rest} 20 + /> 21 + {label && ( 22 + <span className="text-sm text-zinc-700 dark:text-zinc-300"> 23 + {label} 24 + </span> 25 + )} 26 + </label> 27 + ); 28 + }