Skip to content

Commit a9725e9

Browse files
committed
Improve move/copy thread control input folder selection
Groups folders across groups with some "intelligent" default suggestions and preferring those pinned or main columns. Next iteration will store historical folders used and present those as the top options.
1 parent 26eb43e commit a9725e9

File tree

2 files changed

+88
-14
lines changed

2 files changed

+88
-14
lines changed

frontend/src/components/emails/EmailColumnThread.tsx

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,17 @@ import controlStore from "../../stores/control.js";
1313
import { Thread } from "../../stores/emails/base.ts";
1414
import { getEmailStore } from "../../stores/emails/controller.ts";
1515
import mainEmailStore from "../../stores/emails/main.ts";
16-
import filterStore from "../../stores/filters.ts";
1716
import requestStore from "../../stores/request.ts";
1817
import settingsStore from "../../stores/settings.ts";
1918
import threadStore from "../../stores/thread.ts";
2019
import { getAccountIconName } from "../../util/accounts.js";
2120
import {
22-
capitalizeFirstLetter,
2321
formatAddress,
2422
formatDate,
2523
hexToRgb,
2624
} from "../../util/string.js";
2725
import {
26+
buildMoveFolderOptions,
2827
getMoveDataFromThreadComponent,
2928
getThreadColumnMessageIds,
3029
moveOrCopyThread,
@@ -404,19 +403,8 @@ export default class EmailColumnThread extends React.Component<
404403
moveOrCopyThread(moveData, value.value, keyboard.setMovingCurrentThread);
405404
};
406405

407-
const folderOptions = Array.prototype.concat(
408-
_.map(filterStore.props.folderNames, (folderName) => ({
409-
value: folderName,
410-
label: folderName,
411-
})),
412-
_.map(ALIAS_FOLDERS, (folderName) => ({
413-
value: folderName,
414-
label: capitalizeFirstLetter(folderName),
415-
}))
416-
);
417-
418406
controlStore.open(controlInputHandler, {
419-
selectOptions: folderOptions,
407+
selectOptions: buildMoveFolderOptions(this.props.columnId),
420408
header: (
421409
<span>
422410
Move <strong>{subject}</strong>...

frontend/src/util/threads.ts

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,13 @@ import _ from "lodash";
22

33
import EmailColumn from "../components/emails/EmailColumn.tsx";
44
import EmailColumnThread from "../components/emails/EmailColumnThread.tsx";
5+
import { ALIAS_FOLDERS } from "../constants.ts";
56
import { getColumnStore } from "../stores/columns.ts";
67
import { getEmailStore } from "../stores/emails/controller.ts";
8+
import filterStore from "../stores/filters.ts";
79
import requestStore from "../stores/request.ts";
810
import settingsStore from "../stores/settings.ts";
11+
import { capitalizeFirstLetter } from "./string.js";
912

1013
export function moveOrCopyThread(
1114
moveData,
@@ -26,11 +29,15 @@ export function moveOrCopyThread(
2629
? emailStore.copyEmails
2730
: emailStore.moveEmails;
2831

32+
const sourceColumnStore = getColumnStore(oldColumn);
33+
sourceColumnStore.hideThread(thread);
34+
2935
const targetColumnStore = getColumnStore(targetFolder);
3036
targetColumnStore.addIncomingThread(thread);
3137

3238
const undoMove = () => {
3339
moveData.sourceThreadComponent.undoSetIsMoving();
40+
sourceColumnStore.showThread(thread);
3441
targetColumnStore.removeIncomingThread(thread);
3542
};
3643

@@ -161,3 +168,82 @@ export const getNextColumnThreadComponent = (thread) =>
161168
getColumnThreadComponent(thread, "getNextColumn");
162169
export const getPreviousColumnThreadComponent = (thread) =>
163170
getColumnThreadComponent(thread, "getPreviousColumn");
171+
172+
// Context-aware suggested folders based on where the email currently is
173+
const SUGGESTED_TARGETS: Record<string, string[]> = {
174+
inbox: ["archive", "trash", "junk"],
175+
archive: ["inbox", "trash"],
176+
trash: ["inbox", "archive"],
177+
junk: ["inbox", "trash"],
178+
sent: ["archive", "trash"],
179+
drafts: ["inbox", "trash"],
180+
};
181+
182+
function makeFolderOption(folderName: string) {
183+
const isAlias = ALIAS_FOLDERS.includes(folderName);
184+
return {
185+
value: folderName,
186+
label: isAlias ? capitalizeFirstLetter(folderName) : folderName,
187+
};
188+
}
189+
190+
/*
191+
Build a smart-ordered list of folder options for move/copy, grouped by relevance:
192+
1. Suggested — context-aware picks based on the source folder
193+
2. Columns — folders in the user's current column layout
194+
3. Other — everything else, alphabetically sorted
195+
The current folder is excluded from all groups.
196+
*/
197+
export function buildMoveFolderOptions(currentFolder: string) {
198+
const allFolders = _.uniq([
199+
...ALIAS_FOLDERS,
200+
...filterStore.props.folderNames,
201+
]);
202+
203+
// Remove the folder the thread is already in
204+
const available = allFolders.filter((f) => f !== currentFolder);
205+
206+
const suggested = (SUGGESTED_TARGETS[currentFolder] || []).filter((f) =>
207+
available.includes(f)
208+
);
209+
210+
const columns = settingsStore.getCurrentColumns().filter(
211+
(f) => available.includes(f) && !suggested.includes(f)
212+
);
213+
214+
const sidebarFolders = (settingsStore.props.sidebarFolders || []).filter(
215+
(f) => available.includes(f) && !suggested.includes(f) && !columns.includes(f)
216+
);
217+
218+
const placed = new Set([...suggested, ...columns, ...sidebarFolders]);
219+
const other = available.filter((f) => !placed.has(f)).sort();
220+
221+
const groups: { label: string; options: { value: string; label: string }[] }[] = [];
222+
223+
if (suggested.length > 0) {
224+
groups.push({
225+
label: "Suggested",
226+
options: suggested.map(makeFolderOption),
227+
});
228+
}
229+
if (columns.length > 0) {
230+
groups.push({
231+
label: "Columns",
232+
options: columns.map(makeFolderOption),
233+
});
234+
}
235+
if (sidebarFolders.length > 0) {
236+
groups.push({
237+
label: "Sidebar",
238+
options: sidebarFolders.map(makeFolderOption),
239+
});
240+
}
241+
if (other.length > 0) {
242+
groups.push({
243+
label: "Other",
244+
options: other.map(makeFolderOption),
245+
});
246+
}
247+
248+
return groups;
249+
}

0 commit comments

Comments
 (0)