Add Unscheduled tasks

This commit is contained in:
Vyn 2024-10-08 16:36:01 +02:00
parent 07081bb27b
commit 53b1280115
13 changed files with 134 additions and 12 deletions

1
.gitignore vendored
View file

@ -5,3 +5,4 @@ todo.md
.cache .cache
.nvimrc .nvimrc
CMakeLists.txt.user CMakeLists.txt.user
.slint.lua

View file

@ -39,7 +39,9 @@ class BaseSource
const std::string &getName() const; const std::string &getName() const;
void addDay(const DayData &dayData); void addDay(const DayData &dayData);
void addUnscheduledTask(const TaskItemData &taskData);
Day *day(const Date &date); Day *day(const Date &date);
std::vector<std::unique_ptr<TaskItem>> *unscheduledTasks();
std::vector<std::unique_ptr<Day>> *days(); std::vector<std::unique_ptr<Day>> *days();
TaskItem *getTaskById(int taskId); TaskItem *getTaskById(int taskId);
Event *getEventById(int eventId); Event *getEventById(int eventId);
@ -55,6 +57,7 @@ class BaseSource
int id_ = nextId_++; int id_ = nextId_++;
std::string name_; std::string name_;
std::vector<std::unique_ptr<Day>> days_; std::vector<std::unique_ptr<Day>> days_;
std::vector<std::unique_ptr<TaskItem>> unscheduledTasks_;
bool isDirty_ = false; bool isDirty_ = false;
}; };

View file

@ -33,6 +33,7 @@ class TasksView
TasksView(Mirai *mirai); TasksView(Mirai *mirai);
FilteredDay &operator[](int index); FilteredDay &operator[](int index);
std::vector<TaskItem *> &filteredUnscheduledTasks();
size_t count() const; size_t count() const;
void update(); void update();
@ -49,6 +50,7 @@ class TasksView
private: private:
std::vector<FilteredDay> filteredDays; std::vector<FilteredDay> filteredDays;
std::vector<TaskItem *> filteredUnscheduledTasks_;
Mirai *mirai; Mirai *mirai;
Tags tagsFilter; Tags tagsFilter;

View file

@ -6,6 +6,7 @@
#pragma once #pragma once
#include "BaseSource.h"
#include "DateTime.h" #include "DateTime.h"
#include "Day.h" #include "Day.h"
#include "Event.h" #include "Event.h"
@ -23,13 +24,13 @@ namespace mirai
struct MiraiMarkdownFormatParseResult { struct MiraiMarkdownFormatParseResult {
std::string name; std::string name;
std::vector<DayData> days; std::vector<DayData> days;
std::vector<TaskItemData> unscheduledTasks;
}; };
class TodoMdFormat class TodoMdFormat
{ {
public: public:
static std::string static std::string stringify(BaseSource &source);
stringify(const std::string &name, const std::vector<std::unique_ptr<Day>> &days);
static MiraiMarkdownFormatParseResult parse(const std::string &content); static MiraiMarkdownFormatParseResult parse(const std::string &content);

View file

@ -46,12 +46,23 @@ std::vector<std::unique_ptr<Day>> *BaseSource::days()
return &days_; return &days_;
} }
std::vector<std::unique_ptr<TaskItem>> *BaseSource::unscheduledTasks()
{
return &unscheduledTasks_;
}
void BaseSource::addDay(const DayData &dayData) void BaseSource::addDay(const DayData &dayData)
{ {
days_.push_back(std::make_unique<Day>(this, dayData)); days_.push_back(std::make_unique<Day>(this, dayData));
setDirty(true); setDirty(true);
} }
void BaseSource::addUnscheduledTask(const TaskItemData &taskData)
{
unscheduledTasks_.push_back(std::make_unique<TaskItem>(this, nullptr, taskData));
setDirty(true);
}
TaskItem *BaseSource::getTaskById(int taskId) TaskItem *BaseSource::getTaskById(int taskId)
{ {
for (auto &day : days_) { for (auto &day : days_) {
@ -68,6 +79,11 @@ TaskItem *BaseSource::getTaskById(int taskId)
} }
} }
} }
for (auto &task : *unscheduledTasks()) {
if (task->id() == taskId) {
return task.get();
}
}
return nullptr; return nullptr;
} }

View file

@ -21,7 +21,7 @@ void StdFileSource::save()
throw std::runtime_error("can't create " + getPath()); throw std::runtime_error("can't create " + getPath());
} }
const std::string content = TodoMdFormat::stringify(getName(), *days()); const std::string content = TodoMdFormat::stringify(*this);
file << content; file << content;
file.close(); file.close();
@ -53,6 +53,9 @@ void StdFileSource::load()
TaskItem *newTask = newDay->createTask(taskData); TaskItem *newTask = newDay->createTask(taskData);
} }
} }
for (const auto &taskData : result.unscheduledTasks) {
addUnscheduledTask(taskData);
}
setName(result.name); setName(result.name);
}; };
} // namespace mirai } // namespace mirai

View file

@ -36,6 +36,11 @@ FilteredDay &TasksView::operator[](int index)
return filteredDays.at(index); return filteredDays.at(index);
} }
std::vector<TaskItem *> &TasksView::filteredUnscheduledTasks()
{
return filteredUnscheduledTasks_;
}
size_t TasksView::count() const size_t TasksView::count() const
{ {
return filteredDays.size(); return filteredDays.size();
@ -45,12 +50,23 @@ void TasksView::update()
{ {
auto todayDate = std::format("{:%Y-%m-%d}", std::chrono::system_clock::now()); auto todayDate = std::format("{:%Y-%m-%d}", std::chrono::system_clock::now());
filteredDays.clear(); filteredDays.clear();
filteredUnscheduledTasks_.clear();
for (auto &file : mirai->getSources()) { for (auto &file : mirai->getSources()) {
if (resourcesFilter.size() > 0 && if (resourcesFilter.size() > 0 &&
!vectorUtils::contains(resourcesFilter, file->getName())) { !vectorUtils::contains(resourcesFilter, file->getName())) {
continue; continue;
} }
for (auto &task : *file->unscheduledTasks()) {
if (shouldHideCompletedTasks() && task->getState() == DONE) {
continue;
}
if (tagsFilter.size() > 0 && !vectorUtils::containsAll(tagsFilter, task->getTags())) {
continue;
}
filteredUnscheduledTasks_.push_back(task.get());
}
for (auto &day : *file->days()) { for (auto &day : *file->days()) {
FilteredDay filteredDay{.day = day.get()}; FilteredDay filteredDay{.day = day.get()};
for (auto &task : *day->tasks()) { for (auto &task : *day->tasks()) {

View file

@ -132,13 +132,14 @@ std::string TodoMdFormat::taskToString(const TaskItem &task)
return str; return str;
} }
std::string std::string TodoMdFormat::stringify(BaseSource &source)
TodoMdFormat::stringify(const std::string &name, const std::vector<std::unique_ptr<Day>> &days)
{ {
const std::string &name = source.getName();
const std::vector<std::unique_ptr<Day>> *days = source.days();
std::string result = "# " + name + "\n\n"; std::string result = "# " + name + "\n\n";
std::string currentDate = ""; std::string currentDate = "";
for (const auto &day : days) { for (const auto &day : *days) {
auto &date = day->getDate(); auto &date = day->getDate();
result += result +=
"## " + std::format("{:02d}-{:02d}-{:02d}", date.year, date.month, date.day) + "\n\n"; "## " + std::format("{:02d}-{:02d}-{:02d}", date.year, date.month, date.day) + "\n\n";
@ -162,6 +163,13 @@ TodoMdFormat::stringify(const std::string &name, const std::vector<std::unique_p
result += '\n'; result += '\n';
} }
if (source.unscheduledTasks()->size() > 0) {
result += "## Unscheduled\n\n";
for (const auto &task : *(source.unscheduledTasks())) {
result += taskToString(*task) + '\n';
}
result += '\n';
}
return result; return result;
}; };
@ -181,6 +189,7 @@ MiraiMarkdownFormatParseResult TodoMdFormat::parse(const std::string &content)
std::string currentDateString = ""; std::string currentDateString = "";
std::vector<mirai::DayData> daysData; std::vector<mirai::DayData> daysData;
std::vector<mirai::TaskItemData> unscheduledTasks;
mirai::DayData *currentDay = nullptr; mirai::DayData *currentDay = nullptr;
mirai::EventData *currentEvent = nullptr; mirai::EventData *currentEvent = nullptr;
@ -190,6 +199,10 @@ MiraiMarkdownFormatParseResult TodoMdFormat::parse(const std::string &content)
while (std::getline(contentStream, line)) { while (std::getline(contentStream, line)) {
if (std::string_view{line.data(), 3} == "## ") { if (std::string_view{line.data(), 3} == "## ") {
currentDateString = line.substr(3); currentDateString = line.substr(3);
if (currentDateString == "Unscheduled") {
currentDay = nullptr;
continue;
}
auto dateOpt = mirai::stringToDate(currentDateString); auto dateOpt = mirai::stringToDate(currentDateString);
if (!dateOpt.has_value()) { if (!dateOpt.has_value()) {
throw std::runtime_error("Malformated date"); throw std::runtime_error("Malformated date");
@ -210,6 +223,8 @@ MiraiMarkdownFormatParseResult TodoMdFormat::parse(const std::string &content)
currentEvent->tasks.push_back(taskItemData); currentEvent->tasks.push_back(taskItemData);
} else if (currentDay) { } else if (currentDay) {
currentDay->tasks.push_back(taskItemData); currentDay->tasks.push_back(taskItemData);
} else {
unscheduledTasks.push_back(taskItemData);
} }
} else if (cpputils::string::trim(line) == "") { } else if (cpputils::string::trim(line) == "") {
currentEvent = nullptr; currentEvent = nullptr;
@ -219,6 +234,6 @@ MiraiMarkdownFormatParseResult TodoMdFormat::parse(const std::string &content)
gelinesDuration.printTimeElapsed("getlinesDuration"); gelinesDuration.printTimeElapsed("getlinesDuration");
stringToTaskDuration.printTimeElapsed("stringToTaskDuration"); stringToTaskDuration.printTimeElapsed("stringToTaskDuration");
readMdFormatDuration.printTimeElapsed("Reading MD File duration"); readMdFormatDuration.printTimeElapsed("Reading MD File duration");
return {.name = name, .days = daysData}; return {.name = name, .days = daysData, .unscheduledTasks = unscheduledTasks};
} }
} // namespace mirai } // namespace mirai

View file

@ -36,6 +36,7 @@ UiState::UiState(mirai::Mirai *miraiInstance) : miraiInstance_(miraiInstance), v
{ {
sources_ = std::make_shared<slint::VectorModel<slint::SharedString>>(); sources_ = std::make_shared<slint::VectorModel<slint::SharedString>>();
days_ = std::make_shared<slint::VectorModel<Day>>(); days_ = std::make_shared<slint::VectorModel<Day>>();
unscheduledTasks_ = std::make_shared<slint::VectorModel<TaskData>>();
tags_ = std::make_shared<slint::VectorModel<slint::SharedString>>(); tags_ = std::make_shared<slint::VectorModel<slint::SharedString>>();
taskWindow_->global<Backend>().set_sources(sources_); taskWindow_->global<Backend>().set_sources(sources_);
@ -86,7 +87,7 @@ void UiState::setupTaskWindowCallbacks()
mirai::TodoMdFormat::stringToTask("- [ ] " + std::string(newTaskData.title), dateStr); mirai::TodoMdFormat::stringToTask("- [ ] " + std::string(newTaskData.title), dateStr);
task->setText(taskData.text); task->setText(taskData.text);
if (task->day()->getDate() != SlintDateToMiraiDate(date)) { if (task->day() && task->day()->getDate() != SlintDateToMiraiDate(date)) {
auto newDate = source->day(SlintDateToMiraiDate(date)); auto newDate = source->day(SlintDateToMiraiDate(date));
newDate->createTask({ newDate->createTask({
.text = taskData.text, .text = taskData.text,
@ -109,7 +110,9 @@ void UiState::setupTaskWindowCallbacks()
auto task = auto task =
mirai::TodoMdFormat::stringToTask("- [ ] " + std::string(newTaskData.title), dateStr); mirai::TodoMdFormat::stringToTask("- [ ] " + std::string(newTaskData.title), dateStr);
if (newTaskData.eventId == -1) { if (!newTaskData.scheduled) {
source->addUnscheduledTask(task);
} else if (newTaskData.eventId == -1) {
auto day = source->day(SlintDateToMiraiDate(date)); auto day = source->day(SlintDateToMiraiDate(date));
assert(day); assert(day);
day->createTask(task); day->createTask(task);
@ -280,7 +283,12 @@ void UiState::setupCallbacks()
taskWindow_->set_taskSourceIndex(sourceId); taskWindow_->set_taskSourceIndex(sourceId);
taskWindow_->set_taskId(task->id()); taskWindow_->set_taskId(task->id());
taskWindow_->set_taskTitle(slint::SharedString(task->getText())); taskWindow_->set_taskTitle(slint::SharedString(task->getText()));
taskWindow_->set_taskDate(MiraiDateToSlintDate(task->day()->getDate())); if (task->day() != nullptr) {
taskWindow_->set_scheduled(true);
taskWindow_->set_taskDate(MiraiDateToSlintDate(task->day()->getDate()));
} else {
taskWindow_->set_scheduled(false);
}
taskWindow_->show(); taskWindow_->show();
}); });
@ -411,6 +419,26 @@ void UiState::reloadTasks()
} }
days_ = slintDays; days_ = slintDays;
mainWindow_->global<Backend>().set_visible_tasks(days_); mainWindow_->global<Backend>().set_visible_tasks(days_);
unscheduledTasks_->clear();
for (int taskIndex = 0; taskIndex < view_.filteredUnscheduledTasks().size(); ++taskIndex) {
auto &task = view_.filteredUnscheduledTasks().at(taskIndex);
std::vector<slint::SharedString> tags;
std::transform(
task->getTags().begin(), task->getTags().end(), std::back_inserter(tags),
[&](const std::string &tag) {
return slint::SharedString(tag);
}
);
unscheduledTasks_->push_back({
.sourceId = task->source()->id(),
.id = task->id(),
.title = slint::SharedString(task->getText()),
.checked = task->getState() == mirai::DONE,
.tags = std::make_shared<slint::VectorModel<slint::SharedString>>(tags),
});
}
mainWindow_->global<Backend>().set_unscheduled_tasks(unscheduledTasks_);
} }
void UiState::reloadSources() void UiState::reloadSources()

View file

@ -32,6 +32,7 @@ class UiState
std::shared_ptr<slint::VectorModel<slint::SharedString>> sources_; std::shared_ptr<slint::VectorModel<slint::SharedString>> sources_;
std::shared_ptr<slint::VectorModel<slint::SharedString>> tags_; std::shared_ptr<slint::VectorModel<slint::SharedString>> tags_;
std::shared_ptr<slint::VectorModel<Day>> days_; std::shared_ptr<slint::VectorModel<Day>> days_;
std::shared_ptr<slint::VectorModel<TaskData>> unscheduledTasks_;
slint::ComponentHandle<AppWindow> mainWindow_ = AppWindow::create(); slint::ComponentHandle<AppWindow> mainWindow_ = AppWindow::create();
slint::ComponentHandle<TaskWindow> taskWindow_ = TaskWindow::create(); slint::ComponentHandle<TaskWindow> taskWindow_ = TaskWindow::create();

View file

@ -38,6 +38,7 @@ export global Backend {
in-out property<[string]> sources; in-out property<[string]> sources;
in-out property<[string]> tags; in-out property<[string]> tags;
in-out property<[Day]> visible_tasks; in-out property<[Day]> visible_tasks;
in-out property<[TaskData]> unscheduled-tasks;
callback task_clicked(int, int); callback task_clicked(int, int);
callback source_clicked(int); callback source_clicked(int);

View file

@ -107,6 +107,32 @@ export component MainView inherits Rectangle {
} }
} }
} }
if Backend.unscheduled-tasks.length > 0 : VerticalLayout {
Rectangle {
background: Palette.card-background;
border-radius: 8px;
VerticalLayout {
padding: 16px;
HorizontalLayout {
alignment: start;
VText {
text: "Unscheduled";
color: Palette.foreground;
font-size: 1.2rem;
}
}
for task[taskIndex] in Backend.unscheduled-tasks: VerticalLayout {
padding-top: taskIndex == 0 ? 16px : 0px;
padding-bottom: 8px;
TaskLine {
task: task;
source-index: task.sourceId;
task-index: task.id;
}
}
}
}
}
} }
} }
} }

View file

@ -1,11 +1,12 @@
import { Date, ComboBox } from "std-widgets.slint"; import { Date, ComboBox } from "std-widgets.slint";
import { VTextInput, VButton, VDatePicker, VText, Palette } from "@vynui"; import { VTextInput, VButton, VDatePicker, VText, VCheckBox, Palette } from "@vynui";
import { Backend } from "../Backend.slint"; import { Backend } from "../Backend.slint";
export struct NewTaskData { export struct NewTaskData {
sourceId: int, sourceId: int,
eventId: int, eventId: int,
title: string, title: string,
scheduled: bool,
date: Date date: Date
} }
@ -13,6 +14,7 @@ export struct SaveTaskData {
sourceId: int, sourceId: int,
id: int, id: int,
title: string, title: string,
scheduled: bool,
date: Date, date: Date,
} }
@ -28,6 +30,7 @@ export component TaskWindow inherits Window {
in-out property<int> taskId: -1; in-out property<int> taskId: -1;
in-out property<int> eventId: -1; in-out property<int> eventId: -1;
in-out property<bool> scheduled <=> scheduledInput.checked;
in-out property<Date> taskDate <=> taskDateInput.date; in-out property<Date> taskDate <=> taskDateInput.date;
in-out property<string> taskTitle <=> taskTitleInput.text; in-out property<string> taskTitle <=> taskTitleInput.text;
in-out property<int> taskSourceIndex <=> sourceInput.current-index; in-out property<int> taskSourceIndex <=> sourceInput.current-index;
@ -53,9 +56,13 @@ export component TaskWindow inherits Window {
accepted => { button.clicked() } accepted => { button.clicked() }
} }
scheduledInput := VCheckBox {
text: "Scheduled";
}
taskDateInput := VDatePicker { taskDateInput := VDatePicker {
label: "Date"; label: "Date";
enabled: eventId == -1; enabled: eventId == -1 && scheduledInput.checked;
} }
button := VButton { button := VButton {
@ -66,6 +73,7 @@ export component TaskWindow inherits Window {
sourceId: taskSourceIndex, sourceId: taskSourceIndex,
eventId: eventId, eventId: eventId,
title: taskTitle, title: taskTitle,
scheduled: scheduled,
date: taskDate, date: taskDate,
}); });
} else { } else {
@ -73,6 +81,7 @@ export component TaskWindow inherits Window {
sourceId: taskSourceIndex, sourceId: taskSourceIndex,
id: taskId, id: taskId,
title: taskTitle, title: taskTitle,
scheduled: scheduled,
date: taskDate, date: taskDate,
}); });
} }