fix(mobile): use exact directory matching for session grouping (#1687)
* fix(mobile): use exact directory matching for session grouping The new mobile sessions sheet used startsWith prefix matching to assign sessions to projects, which caused child-directory sessions (e.g. /root/repos/opencode) to be grouped into parent projects (e.g. /root/repos). Switch to exact directory matching (project root or registered worktree paths only) to match the desktop sidebar behavior. Also exclude sub-agent sessions (those with parentID) from the totalSessions badge count so the displayed number reflects only top-level sessions. * fix: align mobile session project matching --------- Co-authored-by: lilyzhaun <lilyzhaun@users.noreply.github.com> Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
This commit is contained in:
committed by
GitHub
co-authored by
lilyzhaun
Bohdan Triapitsyn
parent
12835c7646
commit
307808bec2
@@ -154,6 +154,20 @@ const pathBelongsToRoot = (path: string, root: string): boolean => {
|
||||
);
|
||||
};
|
||||
|
||||
const findExactWorktreeMatch = (project: ProjectMeta, normalizedDirectory: string): WorktreeMetadata | null => (
|
||||
project.worktrees.find((worktree) => normalizePath(worktree.path) === normalizedDirectory) ?? null
|
||||
);
|
||||
|
||||
const projectMatchesExactDirectory = (project: ProjectMeta, normalizedDirectory: string): boolean => (
|
||||
normalizedDirectory === project.path || Boolean(findExactWorktreeMatch(project, normalizedDirectory))
|
||||
);
|
||||
|
||||
const findExactProjectMatch = (projects: ProjectMeta[], directory: string): ProjectMeta | null => {
|
||||
const normalizedDirectory = normalizePath(directory);
|
||||
if (!normalizedDirectory) return null;
|
||||
return projects.find((project) => projectMatchesExactDirectory(project, normalizedDirectory)) ?? null;
|
||||
};
|
||||
|
||||
const sessionMatchesQuery = (session: Session, projectLabel: string, query: string): boolean => {
|
||||
if (!query) return true;
|
||||
const haystack = `${session.title ?? ''} ${session.id} ${getSessionDirectory(session)} ${projectLabel}`.toLowerCase();
|
||||
@@ -662,12 +676,10 @@ export const MobileSessionsSheet: React.FC<MobileSessionsSheetProps> = ({ open,
|
||||
for (const session of sessions) {
|
||||
const directory = getSessionDirectory(session);
|
||||
if (!directory) continue;
|
||||
const node = nodes.find((entry) => {
|
||||
if (pathBelongsToRoot(directory, entry.project.path)) return true;
|
||||
return entry.project.worktrees.some((worktree) => pathBelongsToRoot(directory, worktree.path));
|
||||
});
|
||||
const normalizedDirectory = normalizePath(directory);
|
||||
const node = nodes.find((entry) => projectMatchesExactDirectory(entry.project, normalizedDirectory));
|
||||
if (!node) continue;
|
||||
const matchedWorktree = node.project.worktrees.find((entry) => pathBelongsToRoot(directory, entry.path));
|
||||
const matchedWorktree = findExactWorktreeMatch(node.project, normalizedDirectory);
|
||||
const bucket = matchedWorktree
|
||||
? ensureBucket(node, matchedWorktree.path, matchedWorktree)
|
||||
: ensureBucket(node, node.project.path, null);
|
||||
@@ -677,7 +689,9 @@ export const MobileSessionsSheet: React.FC<MobileSessionsSheetProps> = ({ open,
|
||||
for (const node of nodes) {
|
||||
for (const bucket of node.buckets) {
|
||||
bucket.sessions.sort((a, b) => getSessionTimestamp(b) - getSessionTimestamp(a));
|
||||
node.totalSessions += bucket.sessions.length;
|
||||
for (const session of bucket.sessions) {
|
||||
if (!getParentId(session)) node.totalSessions += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -816,10 +830,7 @@ export const MobileSessionsSheet: React.FC<MobileSessionsSheetProps> = ({ open,
|
||||
// Switching session switches the working directory (handled by
|
||||
// setCurrentSession) — also move the active project so the rest of the app
|
||||
// and the active highlight follow the selected session, not just the draft.
|
||||
const project = projectsMeta.find((entry) => {
|
||||
if (pathBelongsToRoot(directory ?? '', entry.path)) return true;
|
||||
return entry.worktrees.some((worktree) => pathBelongsToRoot(directory ?? '', worktree.path));
|
||||
});
|
||||
const project = findExactProjectMatch(projectsMeta, directory ?? '');
|
||||
if (project) setActiveProjectIdOnly(project.id);
|
||||
void setCurrentSession(session.id, directory);
|
||||
onOpenChange(false);
|
||||
@@ -878,12 +889,9 @@ export const MobileSessionsSheet: React.FC<MobileSessionsSheetProps> = ({ open,
|
||||
const buildSessionContextLabel = React.useCallback(
|
||||
(session: Session): string => {
|
||||
const directory = getSessionDirectory(session);
|
||||
const project = projectsMeta.find((entry) => {
|
||||
if (pathBelongsToRoot(directory, entry.path)) return true;
|
||||
return entry.worktrees.some((worktree) => pathBelongsToRoot(directory, worktree.path));
|
||||
});
|
||||
const project = findExactProjectMatch(projectsMeta, directory);
|
||||
if (!project) return getProjectLabel(directory) || directory;
|
||||
const matchedWorktree = project.worktrees.find((entry) => pathBelongsToRoot(directory, entry.path));
|
||||
const matchedWorktree = findExactWorktreeMatch(project, normalizePath(directory));
|
||||
if (matchedWorktree?.branch) return `${project.label} · ${matchedWorktree.branch}`;
|
||||
return project.label;
|
||||
},
|
||||
@@ -915,10 +923,7 @@ export const MobileSessionsSheet: React.FC<MobileSessionsSheetProps> = ({ open,
|
||||
return sessions
|
||||
.filter((session) => {
|
||||
const directory = getSessionDirectory(session);
|
||||
const project = projectsMeta.find((entry) => {
|
||||
if (pathBelongsToRoot(directory, entry.path)) return true;
|
||||
return entry.worktrees.some((worktree) => pathBelongsToRoot(directory, worktree.path));
|
||||
});
|
||||
const project = findExactProjectMatch(projectsMeta, directory);
|
||||
return sessionMatchesQuery(session, project?.label ?? '', normalizedQuery);
|
||||
})
|
||||
.sort((a, b) => getSessionTimestamp(b) - getSessionTimestamp(a));
|
||||
@@ -931,9 +936,9 @@ export const MobileSessionsSheet: React.FC<MobileSessionsSheetProps> = ({ open,
|
||||
.map((project) => ({
|
||||
...project,
|
||||
sessionCount: sessions.filter((session) => {
|
||||
const directory = getSessionDirectory(session);
|
||||
if (pathBelongsToRoot(directory, project.path)) return true;
|
||||
return project.worktrees.some((worktree) => pathBelongsToRoot(directory, worktree.path));
|
||||
if (getParentId(session)) return false;
|
||||
const directory = normalizePath(getSessionDirectory(session));
|
||||
return projectMatchesExactDirectory(project, directory);
|
||||
}).length,
|
||||
}));
|
||||
}, [normalizedQuery, projectsMeta, sessions]);
|
||||
|
||||
Reference in New Issue
Block a user