Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/@types/Todo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export interface TodoObject {

priority: string | null;
hidden: boolean;
inprogress: string | null;
due: string | null;
dueString: string | null;
t: string | null;
Expand Down Expand Up @@ -50,6 +51,7 @@ export type AttributeKey =
| "rec"
| "pm"
| "hidden"
| "inprogress"
| "created"
| "completed";

Expand Down
1 change: 1 addition & 0 deletions src/@types/UI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export interface PromptItem {
export interface ContextMenuItem {
id: string;
label: string;
hint?: string;
promptItem?: PromptItem;
function?: () => void;
}
Expand Down
4 changes: 4 additions & 0 deletions src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
"remove": "Remove",
"add": "Add",
"archive": "Archive",
"markInProgress": "Mark in progress",
"removeInProgress": "Remove in progress",
"middleClick": "Middle-click",
"settings": "Settings",
"close": "Close",
"attributes": "Attributes",
Expand Down Expand Up @@ -80,6 +83,7 @@
"shared.attributeMapping.pm": "Pomodoros",
"shared.attributeMapping.created": "Creation date",
"shared.attributeMapping.completed": "Completion date",
"shared.attributeMapping.inprogress": "In progress",
"splashscreen.noFiles.text": "Open an existing todo.txt file or create a new one",
"splashscreen.noTodosAvailable.text": "No todos in this file",
"splashscreen.noTodosAvailable.create": "Create a todo",
Expand Down
1 change: 1 addition & 0 deletions src/main/Attributes.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ function todo(overrides: Partial<TodoObject> = {}): TodoObject {
notify: false,
hidden: false,
complete: false,
inprogress: null,
lineNumber: 1,
body: "",
string: "",
Expand Down
1 change: 1 addition & 0 deletions src/main/Attributes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ let attributes: Attributes = {
rec: {},
pm: {},
hidden: {},
inprogress: {},
created: {},
completed: {},
};
Expand Down
13 changes: 13 additions & 0 deletions src/main/DataRequest/AttributeModifier.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { expect, describe, it } from "vitest";
import { deleteAttributeValue } from "./AttributeModifier";

describe("deleteAttributeValue", () => {
it("removes inprogress regardless of the passed value", () => {
const lines = ["Todo 1 inprogress:1", "Todo 2"];

const { count } = deleteAttributeValue(lines, "inprogress", "In progress");

expect(count).toBe(1);
expect(lines).toEqual(["Todo 1", "Todo 2"]);
});
});
10 changes: 10 additions & 0 deletions src/main/DataRequest/AttributeModifier.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,16 @@ export function deleteAttributeValue(
}
break;
}
case "inprogress": {
// A flag, removed regardless of the value, which the sidebar passes as its label.
const item = new Item(newLine);
if (item.extensions().some((e) => e.key === "inprogress")) {
item.removeExtension("inprogress");
newLine = item.toString();
modified = true;
}
break;
}
case "created":
case "completed": {
const item = new Item(newLine);
Expand Down
4 changes: 4 additions & 0 deletions src/main/DataRequest/ChangeCompleteState.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ describe("changeCompleteState", () => {
changeCompleteState(todo, true);
expect(createRecurringTodo).toHaveBeenCalledWith(todo, "1b");
});
it("removes inprogress extension when marking complete", () => {
const r = changeCompleteState("My todo inprogress:1", true);
expect(r).not.toContain("inprogress:1");
});
});

describe("change to uncompleted", () => {
Expand Down
2 changes: 2 additions & 0 deletions src/main/DataRequest/ChangeCompleteState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ function changeCompleteState(string: string, state: boolean): string {
JsTodoTxtObject.setPriority(null);
JsTodoTxtObject.setExtension("pri", currentPriority);
}

JsTodoTxtObject.removeExtension("inprogress");
} else {
JsTodoTxtObject.setCompleted(null);
restorePreviousPriority(JsTodoTxtObject);
Expand Down
11 changes: 11 additions & 0 deletions src/main/DataRequest/CreateTodoObjects.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,17 @@ describe("createTodoObject", () => {
});
});

describe("create in-progress todo", () => {
it("sets inprogress to 1", () => {
const todo = createTodoObject(1, "I am in progress inprogress:1");
expect(todo.inprogress).toBe("1");
});
it("sets inprogress to null for other values", () => {
const todo = createTodoObject(1, "I am in progress inprogress:2");
expect(todo.inprogress).toBeNull();
});
});

describe("create todo with updates", () => {
it("updates priority", () => {
const todo = createTodoObject(1, "(B) todo", "priority", "A");
Expand Down
6 changes: 6 additions & 0 deletions src/main/DataRequest/CreateTodoObjects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,11 @@ function createTodoObject(
const hidden = extensions.some(
(extension) => extension.key === "h" && extension.value === "1",
);
const inprogress = extensions.some(
(extension) => extension.key === "inprogress" && extension.value === "1",
)
? "1"
: null;
const stringPm: string | null =
extensions.find((extension) => extension.key === "pm")?.value || null;
const rec =
Expand Down Expand Up @@ -111,6 +116,7 @@ function createTodoObject(
tString,
rec,
hidden,
inprogress,
pm,
string: content,
};
Expand Down
2 changes: 2 additions & 0 deletions src/main/DataRequest/DataRequest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ function dataRequest(passedSearchString: string = ""): RequestedData | null {
rec: {},
pm: {},
hidden: {},
inprogress: {},
created: {},
completed: {},
},
Expand All @@ -62,6 +63,7 @@ function dataRequest(passedSearchString: string = ""): RequestedData | null {
rec: [],
pm: [],
hidden: [],
inprogress: [],
created: [],
completed: [],
},
Expand Down
13 changes: 13 additions & 0 deletions src/main/DataRequest/Sort.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -268,3 +268,16 @@ describe("rec sorting", () => {
});
});
});

describe("inprogress sorting", () => {
it("sorts in-progress todos last when inverted", () => {
const todos = [
createTodoObject(1, "Todo 1 inprogress:1"),
createTodoObject(2, "Todo 2"),
];
todos.sort((a, b) =>
sortTodoObjects(a, b, [{ value: "inprogress", invert: true }]),
);
expect(todos.map((todo) => todo.lineNumber)).toEqual([2, 1]);
});
});
4 changes: 4 additions & 0 deletions src/main/DataRequest/Sort.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,10 @@ const sortTodoObjects = (
} else {
compareResult = invert ? keyB - keyA : keyA - keyB;
}
} else if (value === "inprogress") {
// A flag has only one value to order by, so invert moves in-progress todos last instead
compareResult =
compareValues(a.inprogress, b.inprogress, false) * (invert ? -1 : 1);
} else {
compareResult = compareValues(a[value], b[value], invert);
}
Expand Down
10 changes: 10 additions & 0 deletions src/main/Stores.ts
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,16 @@ const migrations = {
if (config.get("welcomeScreenShown") === undefined) {
config.set("welcomeScreenShown", false);
}

const sorting = config.get("sorting");
config.set("sorting", [
...sorting,
{ id: String(sorting.length + 1), value: "inprogress", invert: false },
]);
config.set("accordionOpenState", [
...config.get("accordionOpenState"),
false,
]);
},
};

Expand Down
13 changes: 12 additions & 1 deletion src/renderer/ContextMenu.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import React, { memo, useRef, useEffect } from "react";
import Menu from "@mui/material/Menu";
import MenuItem from "@mui/material/MenuItem";
import ListItemText from "@mui/material/ListItemText";
import Typography from "@mui/material/Typography";
import "./ContextMenu.scss";
import { ContextMenu, ContextMenuItem, PromptItem } from "@sleek-types";

Expand Down Expand Up @@ -58,7 +60,16 @@ const ContextMenuComponent: React.FC<ContextMenuComponentProps> = memo(
data-testid={`contextMenu-item-${contextMenuItem.id}`}
onClick={() => onClick(contextMenuItem)}
>
{contextMenuItem.label}
<ListItemText>{contextMenuItem.label}</ListItemText>
{contextMenuItem.hint && (
<Typography
variant="body2"
color="text.secondary"
sx={{ ml: 3 }}
>
{contextMenuItem.hint}
</Typography>
)}
</MenuItem>
))}
</Menu>
Expand Down
8 changes: 5 additions & 3 deletions src/renderer/Drawer/Attributes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,11 @@ function buildDisplayBuckets(

for (const [rawKey, entry] of Object.entries(rawGroup)) {
const bucketNames =
settings.useHumanFriendlyDates && isDate
? friendlyDate(rawKey, attributeKey, settings, t)
: [rawKey];
attributeKey === "inprogress"
? [t("shared.attributeMapping.inprogress")]
: settings.useHumanFriendlyDates && isDate
? friendlyDate(rawKey, attributeKey, settings, t)
: [rawKey];

for (const bucketName of bucketNames) {
if (!buckets[bucketName]) {
Expand Down
6 changes: 5 additions & 1 deletion src/renderer/Grid/Group.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, { memo } from "react";
import ListItem from "@mui/material/ListItem";
import Divider from "@mui/material/Divider";
import { useTranslation } from "react-i18next";
import { HandleFilterSelect, IsSelected } from "../Shared";
import { AttributeKey, Filters, SnackbarAction } from "@sleek-types";
import { useAttributeContextMenu } from "../Shared/useAttributeContextMenu";
Expand All @@ -24,6 +25,7 @@ const Group: React.FC<GroupProps> = memo(
settings,
onNotification,
}) => {
const { t } = useTranslation();
const { handleContextMenu } = useAttributeContextMenu({
setContextMenu,
settings,
Expand Down Expand Up @@ -68,7 +70,9 @@ const Group: React.FC<GroupProps> = memo(
onContextMenu={(e) => handleContextMenu(e, value, attributeKey)}
data-testid={`datagrid-group-button-${attributeKey}`}
>
{value}
{attributeKey === "inprogress"
? t("shared.attributeMapping.inprogress")
: value}
</button>
</div>
);
Expand Down
6 changes: 6 additions & 0 deletions src/renderer/Grid/Renderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@ const RendererComponent: React.FC<RendererComponentProps> = memo(
{ pattern: /(?:^|\s)@(\S+)/, type: "contexts", key: "@" },
{ pattern: /(?:^|\s)\+(\S+)/, type: "projects", key: "+" },
{ pattern: /(?:^|\s)h:1\b/, type: "hidden", key: "h:1" },
{
pattern: /(?:^|\s)inprogress:1\b/,
type: "inprogress",
key: "inprogress:1",
},
{ pattern: /(?:^|\s)pm:(\d+)/, type: "pm", key: "pm:" },
{ pattern: /(?:^|\s)rec:([^ ]+)/, type: "rec", key: "rec:" },
{
Expand Down Expand Up @@ -217,6 +222,7 @@ const RendererComponent: React.FC<RendererComponentProps> = memo(
),

hidden: () => null as React.ReactNode,
inprogress: () => null as React.ReactNode,

url: (value) => (
<a
Expand Down
3 changes: 3 additions & 0 deletions src/renderer/Grid/Row.scss
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@
&[data-hidden="true"] {
filter: opacity(50%) grayscale(100%);
}
&[data-inprogress="1"] {
border-left-color: var(--semantic-warning);
}
&:last-child {
border-bottom-left-radius: 0.25em;
}
Expand Down
Loading