sync: update from private repo (e7c4a56)
CI / build-and-test (push) Has been cancelled

This commit is contained in:
oss-sync
2026-06-09 09:43:05 +00:00
parent 483464597a
commit 2bab882d08
8 changed files with 125 additions and 12 deletions
+2 -1
View File
@@ -2,6 +2,7 @@ import { useState, useDeferredValue } from 'react';
import { useQuery, useQueryClient } from '@tanstack/react-query';
import { LocalTask, LocalFileEntry, SubtaskActivity, Visibility, fetchMyOrgs, updateLocalTask } from '../../api';
import { relativeTime } from '../../lib/utils';
import { ownerDisplayName } from '../../lib/owner';
import { DetailTabId } from '../../lib/urlState';
import { DetailHeader } from './DetailHeader';
import { ContinueWithPieceDialog } from './ContinueWithPieceDialog';
@@ -179,7 +180,7 @@ export function LocalDetailPanel({
{task && (
<>
<div className="mb-2 flex items-center gap-2 text-2xs text-slate-500 flex-wrap">
<span>: <b>{task.ownerName ?? 'system'}</b></span>
<span>: <b>{ownerDisplayName(task.ownerId, task.ownerName)}</b></span>
<span>·</span>
<span>{relativeTime(task.createdAt)}</span>
{task.visibility === 'private' && <span>· 🔒 </span>}
+2 -5
View File
@@ -1,6 +1,7 @@
import { memo } from 'react';
import { LocalTask } from '../../api';
import { relativeTime, statusTone, formatStatusLabel } from '../../lib/utils';
import { ownerDisplayName } from '../../lib/owner';
interface LocalTaskListItemProps {
task: LocalTask;
@@ -46,11 +47,7 @@ export const LocalTaskListItem = memo(function LocalTaskListItem({ task, active,
<div className="mt-1.5 flex items-center gap-1.5 text-[10px]">
<span className="font-mono text-slate-400 tabular-nums">{relativeTime(task.updatedAt)}</span>
<span className="text-slate-300">·</span>
{task.ownerId ? (
<span className="text-slate-600">{task.ownerName ?? 'user'}</span>
) : (
<span className="text-slate-400">system</span>
)}
<span className="text-slate-600">{ownerDisplayName(task.ownerId, task.ownerName)}</span>
{task.visibility === 'private' && (
<span className="px-1 rounded text-[10px] font-medium bg-amber-50 dark:bg-amber-500/15 text-amber-700 dark:text-amber-300 border border-amber-100 dark:border-amber-500/30" title="Private">private</span>
)}
+21
View File
@@ -0,0 +1,21 @@
import { describe, it, expect } from 'vitest';
import { ownerDisplayName } from './owner';
describe('ownerDisplayName', () => {
it('shows the real user name when present', () => {
expect(ownerDisplayName('u123', 'Alice')).toBe('Alice');
});
it('shows "local" for the no-auth local owner', () => {
expect(ownerDisplayName('local', null)).toBe('local');
});
it('shows "local" for legacy NULL owner (no-auth rows created before the owner fix)', () => {
expect(ownerDisplayName(null, null)).toBe('local');
expect(ownerDisplayName(undefined, undefined)).toBe('local');
});
it('falls back to "system" for an owner id with no matching user (e.g. deleted account)', () => {
expect(ownerDisplayName('u-deleted', null)).toBe('system');
});
});
+19
View File
@@ -0,0 +1,19 @@
/**
* Resolve the display name for a task / scheduled-task owner.
*
* - A real user name (resolved from the users JOIN) is shown as-is.
* - No-auth single-user deployments own rows under 'local' (new rows) or NULL
* (legacy rows created before owner registration was fixed). Both render as
* 'local' so the lone local operator is shown consistently — instead of the
* misleading 'system' / 'user' labels the views used to fall back to.
* - An owner id with no matching user record (e.g. a deleted account in an auth
* deployment) falls back to 'system'.
*/
export function ownerDisplayName(
ownerId: string | null | undefined,
ownerName: string | null | undefined,
): string {
if (ownerName) return ownerName;
if (ownerId == null || ownerId === 'local') return 'local';
return 'system';
}
+3 -6
View File
@@ -5,6 +5,7 @@ import { EmptyState } from '../components/shared/EmptyState';
import { StatChip } from '../components/shared/StatChip';
import { usePieceList } from '../hooks/usePieces';
import { resolvePieceOptions } from '../lib/splitPieces';
import { ownerDisplayName } from '../lib/owner';
import { fetchMyOrgs, listBrowserSessionProfiles, type Visibility } from '../api';
import { useAuthState } from '../App';
@@ -515,11 +516,7 @@ function ScheduleListItem({ task, active, onClick }: { task: ScheduledTask; acti
</div>
{showOwnership && (
<div className="mt-1.5 flex items-center gap-1.5 text-[10px] flex-wrap">
{task.ownerId ? (
<span className="text-slate-600">{task.ownerName ?? 'user'}</span>
) : (
<span className="text-slate-400">system</span>
)}
<span className="text-slate-600">{ownerDisplayName(task.ownerId, task.ownerName)}</span>
{task.visibility === 'private' && (
<span className="px-1 rounded text-[10px] font-medium bg-amber-50 dark:bg-amber-500/15 text-amber-700 dark:text-amber-300 border border-amber-100 dark:border-amber-500/30" title="非公開">🔒 </span>
)}
@@ -715,7 +712,7 @@ function ScheduleDetailPane({
<div>
<dt className="text-2xs font-medium text-slate-500 mb-1"></dt>
<dd className="text-slate-900">
{task.ownerId ? (task.ownerName ?? 'user') : <span className="text-slate-400">system</span>}
{ownerDisplayName(task.ownerId, task.ownerName)}
</dd>
</div>
<div>