sendoff panel keyed (503 -> 494)

The example paths move from <span> to <code>. A path is user content and must not be
translated; <code> says so semantically, matches how ApiKeys and the Google guide
already render paths, and the scanner skips code elements for that exact reason.

A second sentence about turning someone off was multi-line, so the scanner never saw
it — converted on sight.
This commit is contained in:
2026-08-23 14:21:54 +00:00
parent c8fb89122e
commit 3e07dad69c
@@ -2,6 +2,7 @@ import { useRef, useState } from 'react';
import { Laptop, Send, Upload, User, X } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { useSendOff, useSendOffSenders, useSendOffTargets } from './useSendOff';
import { useTranslation } from '../../locales';
const label = (p: { name: string | null; email: string }): string => p.name || p.email.split('@')[0] || p.email;
const kb = (n: number): string =>
@@ -17,6 +18,7 @@ type Target = { kind: 'machine'; name: string } | { kind: 'user'; id: number; na
* needed keeping in sync.
*/
export const SendOffPanel = () => {
const { t } = useTranslation();
const { devices, people, myFolder, isLoading } = useSendOffTargets();
const send = useSendOff();
const [files, setFiles] = useState<File[]>([]);
@@ -41,7 +43,7 @@ export const SendOffPanel = () => {
<div className="flex h-full min-h-0 flex-col overflow-y-auto p-5">
<div className="mx-auto flex w-full max-w-2xl flex-col gap-5">
<section>
<h3 className="text-sm font-semibold text-foreground">Files</h3>
<h3 className="text-sm font-semibold text-foreground">{t('sendOff.files')}</h3>
<input
ref={picker}
type="file"
@@ -70,7 +72,7 @@ export const SendOffPanel = () => {
<span className="shrink-0 text-xs text-muted-foreground">{kb(f.size)}</span>
<button
type="button"
aria-label={`Remove ${f.name}`}
aria-label={t('sendOff.removeFile', { name: f.name })}
className="shrink-0 text-muted-foreground hover:text-foreground"
onClick={() => setFiles((prev) => prev.filter((_, k) => k !== i))}
>
@@ -86,9 +88,9 @@ export const SendOffPanel = () => {
</section>
<section>
<h3 className="text-sm font-semibold text-foreground">Where</h3>
<h3 className="text-sm font-semibold text-foreground">{t('sendOff.where')}</h3>
{isLoading ? (
<p className="mt-2 text-sm text-muted-foreground">Loading…</p>
<p className="mt-2 text-sm text-muted-foreground">{t('sendOff.loading')}</p>
) : (
<div className="mt-2 flex flex-col gap-3">
<TargetGroup
@@ -124,19 +126,19 @@ export const SendOffPanel = () => {
<section className="flex flex-col gap-2">
<Button onClick={doSend} disabled={!ready || send.isPending} size="lg">
<Send className="mr-2 h-4 w-4" />
{send.isPending ? 'Sending…' : target ? `Send to ${target.name}` : 'Send'}
{send.isPending ? 'Sending…' : target ? t('sendOff.sendTo', { name: target.name }) : 'Send'}
</Button>
{/* The server's own message, not a generic one: a file over the limit and a recipient who has
revoked you are different problems with different answers. */}
{target?.kind === 'user' && myFolder && (
<p className="text-xs text-muted-foreground">
Lands in their <span className="text-foreground">Documents/SendOff/{myFolder}</span>.
{t('sendOff.landsIn')} <code className="text-foreground">Documents/SendOff/{myFolder}</code>.
</p>
)}
{send.error && <p className="text-sm text-destructive">{send.error.message}</p>}
{send.data && (
<p className="text-sm text-muted-foreground">
Landed in <span className="text-foreground">{send.data.path}</span> —{' '}
{t('sendOff.landedIn')} <span className="text-foreground">{send.data.path}</span> —{' '}
{send.data.files.map((f) => f.name).join(', ')}
</p>
)}
@@ -195,15 +197,16 @@ const TargetGroup = ({ icon: Icon, heading, empty, options, selected, onSelect }
* the answer has to be visible in the same place the question was asked.
*/
const WhoMaySend = () => {
const { t } = useTranslation();
const { candidates, isLoading, setAllowed, isSaving } = useSendOffSenders();
if (isLoading || candidates.length === 0) return null;
return (
<section className="border-t border-border pt-4">
<h3 className="text-sm font-semibold text-foreground">Who may send to me</h3>
<h3 className="text-sm font-semibold text-foreground">{t('sendOff.whoMaySend')}</h3>
<p className="mt-1 text-xs text-muted-foreground">
Files from anyone you allow land in <span className="text-foreground">Documents/SendOff/their-name</span>.
Turning someone off stops future sends and leaves what they already sent alone.
{t('sendOff.allowedLandIn')} <code className="text-foreground">Documents/SendOff/their-name</code>.{' '}
{t('sendOff.turningOff')}
</p>
<div className="mt-2 flex flex-col gap-1">
{candidates.map((p) => (