the folder name is served, not derived three times
Mobile asked for `/targets` to carry each person's display name as the folder will be named, so a picker can honestly say where files will appear. Right request: the writer already computes that exact string to create a directory, and any client computing its own would eventually disagree with it — which means telling somebody the wrong place to look. So `displayNameFor` is the one definition and the writer calls it too. `/targets` gains `displayName` per person, and `myFolder` for the whole response: files land in the RECIPIENT's tree under the SENDER's name, so that is one value rather than a field on each row. The two are easy to confuse and the distinction matters — a picker showing `displayName` as the destination folder would name the wrong one. The web panel now uses both instead of its own copy of the fallback. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -17,7 +17,7 @@ type Target = { kind: 'machine'; name: string } | { kind: 'user'; id: number; na
|
||||
* needed keeping in sync.
|
||||
*/
|
||||
export const SendOffPanel = () => {
|
||||
const { devices, people, isLoading } = useSendOffTargets();
|
||||
const { devices, people, myFolder, isLoading } = useSendOffTargets();
|
||||
const send = useSendOff();
|
||||
const [files, setFiles] = useState<File[]>([]);
|
||||
const [target, setTarget] = useState<Target | null>(null);
|
||||
@@ -107,10 +107,12 @@ export const SendOffPanel = () => {
|
||||
icon={User}
|
||||
heading="People"
|
||||
empty="Nobody has allowed you to send to them yet."
|
||||
// `displayName` from the server, never computed here: it is the folder the writer will
|
||||
// create, and a picker that guessed it would sooner or later name the wrong directory.
|
||||
options={people.map((p) => ({
|
||||
key: `u:${p.id}`,
|
||||
name: label(p),
|
||||
target: { kind: 'user' as const, id: p.id, name: label(p) },
|
||||
name: p.displayName,
|
||||
target: { kind: 'user' as const, id: p.id, name: p.displayName },
|
||||
}))}
|
||||
selected={target}
|
||||
onSelect={setTarget}
|
||||
@@ -126,6 +128,11 @@ export const SendOffPanel = () => {
|
||||
</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>.
|
||||
</p>
|
||||
)}
|
||||
{send.error && <p className="text-sm text-destructive">{send.error.message}</p>}
|
||||
{send.data && (
|
||||
<p className="text-sm text-muted-foreground">
|
||||
|
||||
@@ -3,7 +3,9 @@ import { useClient } from 'hooks/useClient';
|
||||
|
||||
export type SendOffDevice = { id: string; name: string; platform: string | null; lastSeenAt: string };
|
||||
export type SendOffPerson = { id: number; email: string; name: string | null };
|
||||
type Targets = { devices: SendOffDevice[]; people: SendOffPerson[] };
|
||||
/** As served by `/targets` — `displayName` is the folder name, computed once on the server. */
|
||||
export type SendOffTargetPerson = SendOffPerson & { displayName: string };
|
||||
type Targets = { devices: SendOffDevice[]; people: SendOffTargetPerson[]; myFolder: string };
|
||||
type Senders = { allowed: SendOffPerson[]; candidates: (SendOffPerson & { allowed: boolean })[] };
|
||||
|
||||
const TARGETS = ['sendoff', 'targets'];
|
||||
@@ -19,7 +21,7 @@ export function useSendOffTargets() {
|
||||
// two-row query — the panel is the only reader and it is open for seconds at a time.
|
||||
staleTime: 0,
|
||||
});
|
||||
return { devices: data?.devices ?? [], people: data?.people ?? [], isLoading, error };
|
||||
return { devices: data?.devices ?? [], people: data?.people ?? [], myFolder: data?.myFolder ?? '', isLoading, error };
|
||||
}
|
||||
|
||||
/** Who I have allowed to send to me — the consent list, which only I can change. */
|
||||
|
||||
Reference in New Issue
Block a user