kdheepak / taskwarrior-tui

`taskwarrior-tui`: A terminal user interface for taskwarrior
https://kdheepak.com/taskwarrior-tui
MIT License
1.5k stars 68 forks source link

Request for clarification on behavior. Can "selected task" spontaneously change? #302

Open bradyt opened 3 years ago

bradyt commented 3 years ago

If a task's urgency is a function of time, and tasks are sorted by urgency, is the selection and action stable? What if a filter suggests a task's visibility is a function of time?

Does "selected task" follow a task if taskwarrior-tui finds new urgency values? Does it follow the uuid of a task? For example, with wait:5s. What happens if a feature like until:5s removes the selected task?

In other words, for task add foo +baz wait:5s, the urgency starts out as -2.2, but a few seconds later is 0.8. This can move the task in the taskwarrior-tui view.

If it helps, I think taskwarrior-tui autorefreshes the urgency and ordering about once a minute.

How should users anticipate any spontaneous changes like these?

I tried to do a better job of determining the behavior before filing any related issue, but started to get a little confused about what the exact behavior was, so I thought it would be easier to just ask you.

My attempts at clarifying behavior

I tested with a clean setup like mkdir /tmp/testing && export HOME=/tmp/testing. Intermittently running rm -r /tmp/testing/.task.

I had taskwarrior-tui open in one terminal, with filter set to -completed and -deleted.

In a second terminal, I tried adding tasks like task add foo +bar wait:10s and task add baz, and waiting about a minute.

Similarly for task 1 mod until:10s.

kdheepak commented 3 years ago

That's a great question! The logic for this is implemented here:

https://github.com/kdheepak/taskwarrior-tui/blob/8766b0c64dba57b0fa496cfe36e9d09a5533c16f/src/app.rs#L1259-L1277

This function runs on every update:

https://github.com/kdheepak/taskwarrior-tui/blob/8766b0c64dba57b0fa496cfe36e9d09a5533c16f/src/app.rs#L1254

which happens at every tick, which is by default every 250ms, and this tick rate is configurable. (It only runs update(force = false) though which is mainly for UI updates. update(force = true) runs task export and imports that data into taskwarrior-tui, and this runs whenever there are known changes either to the .task folder or when the user does an action like task add or task delete since it is more time consuming.)

So, selection_fix runs every tick (i.e. 250ms). And it checks if there's a task in the task list where the task uuid is the same as the previous selection or if there's a task in the task list where the task ID is the same as the previous selection. If there is a match, it moves the "selection index" in the UI to that task. If not, it leaves the "selection index" in the same spot as it was before, so visually it wouldn't jump to an unexpected location.

So, if you have a task selected, and the priority or urgency changes, the taskwarrior-tui selection should be "sticky" and it'll remain on the same task. However, if you run task delete or task done, then the previous selection's uuid will not be in the task list. And since taskwarrior-tui finds that they are no matches and doesn't change the "selection index".

Here's an example of running task delete:

https://user-images.githubusercontent.com/1813121/139570229-4574888b-f308-4a9b-915b-f55fb3f47610.mov


I was trying to come up with an example of the first case, and I think I discovered a bug. Let me follow up after doing some experimenting / debugging.

kdheepak commented 3 years ago

Does "selected task" follow a task if taskwarrior-tui finds new urgency values? Does it follow the uuid of a task? For example, with wait:5s. What happens if a feature like until:5s removes the selected task?

Yelp, it turns out it doesn't do what I what it did. Currently only changes inside taskwarrior-tui are tracked and the selection is sticky only in those cases. Meaning, if a change happens "in the background" then the selection may jump unexpectedly. You can try this by running task N delete where N is the task ID of a task above your current selection.

I think the corner case where this happens is unlikely, but I'll still work on a fix. It'd be nice to guarantee that nothing unexpected happens.

kdheepak commented 3 years ago

Okay, I added a fix and I think that'll make such that in all cases when a update(force = true) is run, the selection will be sticky unless taskwarrior-tui deliberately jumps to a different task selection (e.g. in the case of task add description using the TUI).

kdheepak commented 3 years ago

I made a new release that has a fix for the task list updating outside taskwarrior-tui: https://github.com/kdheepak/taskwarrior-tui/releases/tag/v0.14.6

bradyt commented 3 years ago

Okay, I've setup an environment where I can better test released taskwarrior-tui versions and task 2.6.X, though I am still using task 2.5.3 for personal lists as I would like to wait some time before starting to migrate a mobile app to newer task.

I'm still trying to understand all the behavior, but I think at the moment where I'm still confused on is the autorefresh of taskwarrior-tui at about one minute, for example, with tasks spontaneously disappearing with until:5s.

Here is a Dockerfile I've used to try to reproduce in a sterile environment:

FROM debian:sid

RUN apt-get update && apt-get install -y \
    taskwarrior \
    curl

RUN curl -LOJ https://github.com/kdheepak/taskwarrior-tui/releases/download/v0.14.7/taskwarrior-tui-x86_64-unknown-linux-gnu.tar.gz
RUN tar xf taskwarrior-tui-x86_64-unknown-linux-gnu.tar.gz

RUN task rc.confirmation:no calc 1 + 1 # dummy command to create .taskrc

RUN task add foo
RUN task add bar

CMD ./taskwarrior-tui

So this opens to two tasks with focus on the task with description "foo". I then use m to add until:0s to task foo, and about one minute later, the task foo spontaneously disappears and task bar is now selected.

kdheepak commented 3 years ago

Thanks for elaborating on that and for providing minimal working example. Can you expand on why that is confusing? Isn’t that behavior what I explained above? taskwarrior-tui only runs task export every minute during a forced update. So you wouldn’t see the change until a minute later. You can hit r to reload at any point and cause a force update.

bradyt commented 3 years ago

I thought of another scenario. If we have task config uda.taskwarrior-tui.background_process task sync set, and on a second client, a task is deleted and sync called, then in taskwarrior-tui, it is possible to press r, visually verify the selected task, and have the uuid tied to selection change a split second before pressing x.

I created a repo to help make this a more concrete example, especially at commit https://github.com/bradyt/taskwarrior-tui-docker/tree/4ca55644e7e45974ecbecff15dc5f8b195919ca2.

From there, I essentially run the following:

> docker build -t tt . && docker run -it tt
> taskdctl start
> taskwarrior-tui

A few seconds later, the selected uuid changes. I noticed that pressing r did not avoid a spontaneous change here, such as resetting the sync timer.

bradyt commented 3 years ago

Can you expand on why that is confusing? Isn’t that behavior what I explained above?

I wasn't sure how to interpret everything, so I suppose I created a concrete and minimal example to do my best at clarifying. And you clarified it, thank you. I now realize that the one minute autorefresh is explicit and deliberate. I thought at first it might be something random on my system.

bradyt commented 3 years ago

Would it be "easy" to implement a "no selection" state if selected uuid is to be filtered some time longer than a tick after a more user-deliberate uuid change? Presumably a desirable fix would be one that's easy to implement and maintain. And not necessarily a priority. I agree this could be somewhat of a rare corner case. I don't presume to understand this perfectly, just still exploring the app, its behavior, and brainstorming.

Perhaps I'm a little fixated on these aspects of a live view of tasks, as I think the idea is very interesting, and have been considering it for a similar app.

Potentially, user could just avoid setting periodic sync, but that would still leave some motivation to spam the r button to avoid inadvertent edits. A "no selection" feature could alleviate this concern.

bradyt commented 3 years ago

Depending on priority, implementation, maintenance, none of which I know, another idea could be to display a prompt to user that uuid selection will change, or more generally, that order and visibility of list items is about to change... for those at least some ticks after a user-deliberate change.

Though, if uuid selection doesn't change, a blocking prompt sounds like a bad idea.

bradyt commented 3 years ago

Potentially a slow sync like 5 or 10 seconds could be relevant in considering all of this. Not sure if you have a way to simulate slow networks, nor if slow sync is even relevant here. On macOS I'm using Network Link Conditioner.

kdheepak commented 2 years ago

Would it be "easy" to implement a "no selection" state if selected uuid is to be filtered some time longer than a tick after a more user-deliberate uuid change?

Yes. It is fairly easy to implement this. In fact, this block

https://github.com/kdheepak/taskwarrior-tui/blob/0c163e252bc53dbc5ca1f6969dd4ca8e233126b8/src/app.rs#L1381-L1399

is what sets up the switch from Multiple Selection to Single Selection when there are no selected (i.e. marked) tasks.

This is what I imagine is intuitive behavior, which is why it is set up this way. You could compile the code without the calls to this function and it should not select any task when you toggle the selection of the last selected task.

But when no tasks are selected, what do you imagine task edit, task done, task delete, task modify to do? I imagine it can be confusing in that case. Also, I'd have to distinguish between single task hovered + not selected vs single task hovered + selected. Currently when you enter single selection mode, the hovered over task is the selected task. And that is additional complexity for little to no benefit I imagine.

Perhaps I'm a little fixated on these aspects of a live view of tasks, as I think the idea is very interesting, and have been considering it for a similar app.

I've love to see it! And if you have feedback for improving this app, I'd love to hear that too.

Depending on priority, implementation, maintenance, none of which I know, another idea could be to display a prompt to user that uuid selection will change, or more generally, that order and visibility of list items is about to change... for those at least some ticks after a user-deliberate change.

This is unfortunately out of my control. taskwarrior can change task ids whenever it feels like it. uuids though, I think that doesn't ever change. Maybe it would if you deleted a task and then tried to undo or something weird like that, but I don't know enough about how that is implement to comment accurately on that.

You can change the tick time to an arbitrary large number using the configuration settings and use manual refreshes if you'd like. That should address that need.

Potentially a slow sync like 5 or 10 seconds could be relevant in considering all of this.

That's an interesting idea. There's no network here though, everything happens through the local file system.

bradyt commented 2 years ago

Thank you for the out-of-band communication. It clarified a lot for me.

Especially you helped me understood enough to have better nomenclature. So where I was vaguely pointing at background process, and "some ticks later". So the one minute update, whether it is reconfigured to be task sync, or to be set at a different period than one minute, is still just the one background process, and the timer is not reset by any action in the app. And I learned that the tick behavior is watching the .task directory for file changes, which was originally lost on me despite your suggesting the task N delete behavior. I thought the tick was just for the age of task, etc. So I will now try to continue replying with this better understanding of the app and nomenclature.

I mentioned both a "no selection state" as well as a blocking prompt, for the scenario where a selected task is suddenly filtered from view, from a change in the background (shorthand for tick or background process, hope this suffices). Thinking more about it, I suspect the blocking prompt may be preferred to the "no selection state". My reasoning is, the blocking prompt could be an earlier warning/reminder that the selected task has disappeared. This may be a rare occurrence, so I guess a blocking prompt would be fine. Maybe confirming with Enter would be fine? Presumably the user is not doing something that would cause the prompt to disappear too soon.

Although I'm now less in the opinion of switching to a "no selected state", you did ask what would happen then for task edit/done/delete/modify. Well, this is already possible, if the filter returns zero tasks, for example when there are not yet any ~/.task/*.data files yet, or if you try a nonsense filter like / turbofish Enter. I think the current behavior is fine, but I suppose you could have a blocking prompt that reminds the user why they can't currently perform these actions. Note that task edit/done/delete/modify on the command line provides a blocking prompt that there is no filter, and the command will apply to all tasks.

And regarding a hover, if I understand correctly, when there are zero tasks shown by filter, there is a "no hover state". But overall I don't mean to change the behavior of multiple selection versus single selection. It seems good to me. Perhaps there are some subtle implications I'm missing.

Regarding my similar app, it is at my Gihub repo taskw-dart, and there are links to the mobile app store listings, where it is free of charge. I did make a brief attempt at implementing a live view, such as age of task, but quickly deprioritized such a feature when I encountered difficulties. It is a slightly different situation as there is no moving selection, just a thumb tap of a task to go to editing/detail view. But I still noticed difficulty in implication of evolving urgency, sorting, and now I realize, filtering. If I ever make progress on such a feature, I will try to ping you.

Regarding ids and uuids changing, I think I was just being too loose in my nomenclature originally, apologies for miscommunication.

Regarding the lengthening of the tick time, I have to think about that, my understanding of ticks recently changed.

Regarding a slow network, it is not currently relevant, I was mentioning in case some future design included feedback on when a background process would start, or if user was to call it manually. In my own app, I find it a little bit annoying when I press sync, and a slow network causes it to update the UI a moment later, or for the sync to fail altogether, so I'm hoping to implement a progress indicator. The APIs I'm using expose various steps, like connecting, initializing TLS, how many bytes are being sent or received, so it's now just a matter of me dealing with the messy codebase I've created.

kdheepak commented 2 years ago

Thanks for summarizing our discussion and for your detailed follow on thoughts! I think the blocking prompt is an interesting thought and I'll implement that to ensure a user doesn't accidentally perform an action on a task they didn't intend to.