Limits are now account-tied rather than task-tied

This commit is contained in:
alejo committed 2026-10-24 09:13:03 -03:00
1 parent 315a2102a7
commit 0af468f47c
9 files changed
+235 -14

No files matched your search

+82 -5
View File
@@ -62,6 +62,11 @@ void Repl::registerCommands() {
{ArgKind::Account}, &Repl::cmdAccDel);
add("acc rename", "acc rename <name> <new>", "rename an account", 2, 2,
{ArgKind::Account, ArgKind::None}, &Repl::cmdAccRename);
add("acc wait", "acc wait <name> <time>",
"the account hit its limit until <time>: its Prompt/Testing tasks show Wait, then Limit reset",
2, kUnlimited, {ArgKind::Account, ArgKind::None}, &Repl::cmdAccWait);
add("acc unwait", "acc unwait <name>", "clear an account's wait / limit reset (its tasks show their own status again)",
1, 1, {ArgKind::Account}, &Repl::cmdAccUnwait);
add("acc ls", "acc ls", "show the account table", 0, 0, {}, &Repl::cmdAccLs);
add("tsk add", "tsk add <name> [acc] [status]", "create a task", 1, kUnlimited,
@@ -277,6 +282,55 @@ bool Repl::cmdAccRename(const Args& args) {
return true;
}
void Repl::setAccountWait(const std::string& account, std::time_t until) {
db_.setAccountCooldown(account, StatusUtil::timeToText(until));
say("Account '" + account + "' waits until " + StatusUtil::clockLabel(until) +
": its Prompt and Testing tasks show Wait, then Limit reset (acc unwait " + account + " to clear).");
}
// Tells the user when a task's own status is hidden by its account's wait.
void Repl::noteAccountWait(const std::string& taskName) {
std::optional<TaskRow> task = db_.getTask(taskName);
std::optional<Status> stored = task ? StatusUtil::deserialize(task->status) : std::nullopt;
if (!task || !task->account || !stored || !StatusUtil::followsAccountWait(stored->kind)) {
return;
}
std::optional<AccountRow> account = db_.getAccount(*task->account);
if (account && account->cooldown) {
say("It shows as Wait / Limit reset because account '" + account->id + "' is waiting (acc unwait " +
account->id + " to clear).");
}
}
bool Repl::cmdAccWait(const Args& args) {
std::optional<AccountRow> account = db_.getAccount(args[0]);
if (!account) {
throw std::runtime_error("No account named '" + args[0] + "'.");
}
const std::string text = "wait " + StringUtil::join(args, 1, " ");
std::optional<Status> status = StatusUtil::parse(text, std::time(nullptr));
if (!status) {
throw std::runtime_error("Invalid time '" + StringUtil::join(args, 1, " ") +
"'. Use e.g. 16:30, 4:30pm or 4pm.");
}
setAccountWait(account->id, status->waitUntil);
return true;
}
bool Repl::cmdAccUnwait(const Args& args) {
std::optional<AccountRow> account = db_.getAccount(args[0]);
if (!account) {
throw std::runtime_error("No account named '" + args[0] + "'.");
}
if (!account->cooldown) {
say("Account '" + account->id + "' is not waiting.");
return false;
}
db_.setAccountCooldown(account->id, std::nullopt);
say("Wait cleared on '" + account->id + "'.");
return true;
}
bool Repl::cmdAccLs(const Args&) {
Terminal::clearScreen();
std::cout << display_.renderAccounts(std::time(nullptr)) << "\n";
@@ -311,8 +365,18 @@ bool Repl::cmdTskAdd(const Args& args) {
status = parseStatusFrom(args, next);
}
db_.addTask(name, account, StatusUtil::serialize(status));
// A wait on a task that has an account is the account's wait; the task itself
// stays Prompt and follows it.
const bool accountWait = (status.kind == StatusKind::Wait && account);
const Status stored = accountWait ? StatusUtil::make(StatusKind::Prompt) : status;
db_.addTask(name, account, StatusUtil::serialize(stored));
say("Task '" + name + "' added.");
if (accountWait) {
setAccountWait(*account, status.waitUntil);
} else {
noteAccountWait(name);
}
return true;
}
@@ -365,13 +429,26 @@ bool Repl::cmdTskReacc(const Args& args) {
}
bool Repl::cmdTskRestat(const Args& args) {
std::optional<std::string> name = db_.findTask(args[0]);
if (!name) {
std::optional<TaskRow> task = db_.getTask(args[0]);
if (!task) {
throw std::runtime_error("No task named '" + args[0] + "'.");
}
Status status = parseStatusFrom(args, 1);
db_.setTaskStatus(*name, StatusUtil::serialize(status));
say("Task '" + *name + "' set to " + StatusUtil::label(status) + ".");
if (status.kind == StatusKind::Wait && task->account) {
// The wait belongs to the account. Keep an active task as it is so it
// follows the wait; any other status becomes Prompt.
std::optional<Status> current = StatusUtil::deserialize(task->status);
if (!current || !StatusUtil::followsAccountWait(current->kind)) {
db_.setTaskStatus(task->id, StatusUtil::serialize(StatusUtil::make(StatusKind::Prompt)));
}
setAccountWait(*task->account, status.waitUntil);
return true;
}
db_.setTaskStatus(task->id, StatusUtil::serialize(status));
say("Task '" + task->id + "' set to " + StatusUtil::label(status) + ".");
noteAccountWait(task->id);
return true;
}