mirror of https://github.com/zulip/zulip.git
todo_widget: Allow tasks to be added through `/todo` command.
Uptil now, users could add tasks to a todo widget only after creating it through the `/todo` command in the compose box. Users can now add an initial list of tasks using the `/todo` command, with each task on a new line in the compose box, where the 1st `:` would separate a task from its (optional) description. Example: `/todo\nTask1:description1\nTask2 without description`. Fixes part of #20213.
This commit is contained in:
parent
6df3ad251a
commit
d8a8364d1a
|
@ -32,6 +32,7 @@ const poll_widget_extra_data_schema = z
|
|||
export const todo_widget_extra_data_schema = z
|
||||
.object({
|
||||
task_list_title: z.string().optional(),
|
||||
tasks: z.array(z.object({task: z.string(), desc: z.string()})).optional(),
|
||||
})
|
||||
.nullable();
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@ export class TaskData {
|
|||
current_user_id,
|
||||
is_my_task_list,
|
||||
task_list_title,
|
||||
tasks,
|
||||
report_error_function,
|
||||
}) {
|
||||
this.message_sender_id = message_sender_id;
|
||||
|
@ -36,6 +37,14 @@ export class TaskData {
|
|||
} else {
|
||||
this.set_task_list_title($t({defaultMessage: "Task list"}));
|
||||
}
|
||||
|
||||
for (const [i, data] of tasks.entries()) {
|
||||
this.handle.new_task.inbound("canned", {
|
||||
key: i,
|
||||
task: data.task,
|
||||
desc: data.desc,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
set_task_list_title(new_title) {
|
||||
|
@ -219,13 +228,14 @@ export function activate({$elem, callback, extra_data, message}) {
|
|||
return;
|
||||
}
|
||||
const {data} = parse_result;
|
||||
const {task_list_title = ""} = data || {};
|
||||
const {task_list_title = "", tasks = []} = data || {};
|
||||
const is_my_task_list = people.is_my_user_id(message.sender_id);
|
||||
const task_data = new TaskData({
|
||||
message_sender_id: message.sender_id,
|
||||
current_user_id: people.my_current_user_id(),
|
||||
is_my_task_list,
|
||||
task_list_title,
|
||||
tasks,
|
||||
report_error_function: blueslip.warn,
|
||||
});
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ type ZFormExtraData = {
|
|||
// TODO: This TodoWidgetExtraData type should be moved to web/src/todo_widget.js when it will be migrated
|
||||
type TodoWidgetExtraData = {
|
||||
task_list_title?: string;
|
||||
tasks?: {task: string; desc: string}[];
|
||||
};
|
||||
|
||||
type WidgetExtraData = PollWidgetExtraData | TodoWidgetExtraData | ZFormExtraData | null;
|
||||
|
|
|
@ -6,7 +6,7 @@ from zerver.lib.message import SendMessageRequest
|
|||
from zerver.models import Message, SubMessage
|
||||
|
||||
|
||||
def get_widget_data(content: str) -> Tuple[Optional[str], Optional[str]]:
|
||||
def get_widget_data(content: str) -> Tuple[Optional[str], Any]:
|
||||
valid_widget_types = ["poll", "todo"]
|
||||
tokens = re.split(r"\s+|\n+", content)
|
||||
|
||||
|
@ -73,33 +73,9 @@ def parse_todo_extra_data(content: str) -> Any:
|
|||
|
||||
def get_extra_data_from_widget_type(content: str, widget_type: Optional[str]) -> Any:
|
||||
if widget_type == "poll":
|
||||
# This is used to extract the question from the poll command.
|
||||
# The command '/poll question' will pre-set the question in the poll
|
||||
lines = content.splitlines()
|
||||
question = ""
|
||||
options = []
|
||||
if lines and lines[0]:
|
||||
question = lines.pop(0).strip()
|
||||
for line in lines:
|
||||
# If someone is using the list syntax, we remove it
|
||||
# before adding an option.
|
||||
option = re.sub(r"(\s*[-*]?\s*)", "", line.strip(), count=1)
|
||||
if len(option) > 0:
|
||||
options.append(option)
|
||||
extra_data = {
|
||||
"question": question,
|
||||
"options": options,
|
||||
}
|
||||
return extra_data
|
||||
return parse_poll_extra_data(content)
|
||||
else:
|
||||
# This is used to extract the task list title from the todo command.
|
||||
# The command '/todo Title' will pre-set the task list title
|
||||
lines = content.splitlines()
|
||||
task_list_title = ""
|
||||
if lines and lines[0]:
|
||||
task_list_title = lines.pop(0).strip()
|
||||
extra_data = {"task_list_title": task_list_title}
|
||||
return extra_data
|
||||
return parse_todo_extra_data(content)
|
||||
|
||||
|
||||
def do_widget_post_save_actions(send_request: SendMessageRequest) -> None:
|
||||
|
|
|
@ -98,13 +98,17 @@ class WidgetContentTestCase(ZulipTestCase):
|
|||
self.assertEqual(get_widget_data(content=message), (None, None))
|
||||
|
||||
# Add positive checks for context
|
||||
self.assertEqual(get_widget_data(content="/todo"), ("todo", {"task_list_title": ""}))
|
||||
self.assertEqual(
|
||||
get_widget_data(content="/todo Title"), ("todo", {"task_list_title": "Title"})
|
||||
get_widget_data(content="/todo"), ("todo", {"task_list_title": "", "tasks": []})
|
||||
)
|
||||
self.assertEqual(
|
||||
get_widget_data(content="/todo Title"),
|
||||
("todo", {"task_list_title": "Title", "tasks": []}),
|
||||
)
|
||||
# Test tokenization on newline character
|
||||
self.assertEqual(
|
||||
get_widget_data(content="/todo\nignore"), ("todo", {"task_list_title": ""})
|
||||
get_widget_data(content="/todo\nTask"),
|
||||
("todo", {"task_list_title": "", "tasks": [{"task": "Task", "desc": ""}]}),
|
||||
)
|
||||
|
||||
def test_explicit_widget_content(self) -> None:
|
||||
|
@ -171,7 +175,7 @@ class WidgetContentTestCase(ZulipTestCase):
|
|||
|
||||
expected_submessage_content = dict(
|
||||
widget_type="todo",
|
||||
extra_data={"task_list_title": ""},
|
||||
extra_data={"task_list_title": "", "tasks": []},
|
||||
)
|
||||
|
||||
submessage = SubMessage.objects.get(message_id=message.id)
|
||||
|
@ -188,7 +192,42 @@ class WidgetContentTestCase(ZulipTestCase):
|
|||
|
||||
expected_submessage_content = dict(
|
||||
widget_type="todo",
|
||||
extra_data={"task_list_title": "Example Task List Title"},
|
||||
extra_data={"task_list_title": "Example Task List Title", "tasks": []},
|
||||
)
|
||||
|
||||
submessage = SubMessage.objects.get(message_id=message.id)
|
||||
self.assertEqual(submessage.msg_type, "widget")
|
||||
self.assertEqual(orjson.loads(submessage.content), expected_submessage_content)
|
||||
|
||||
# We test for both trailing and leading spaces, along with blank lines
|
||||
# for the tasks.
|
||||
content = "/todo Example Task List Title\n\n task without description\ntask: with description \n\n - task as list : also with description"
|
||||
payload["content"] = content
|
||||
result = self.api_post(sender, "/api/v1/messages", payload)
|
||||
self.assert_json_success(result)
|
||||
|
||||
message = self.get_last_message()
|
||||
self.assertEqual(message.content, content)
|
||||
|
||||
expected_submessage_content = dict(
|
||||
widget_type="todo",
|
||||
extra_data=dict(
|
||||
task_list_title="Example Task List Title",
|
||||
tasks=[
|
||||
dict(
|
||||
task="task without description",
|
||||
desc="",
|
||||
),
|
||||
dict(
|
||||
task="task",
|
||||
desc="with description",
|
||||
),
|
||||
dict(
|
||||
task="task as list",
|
||||
desc="also with description",
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
|
||||
submessage = SubMessage.objects.get(message_id=message.id)
|
||||
|
@ -267,9 +306,7 @@ class WidgetContentTestCase(ZulipTestCase):
|
|||
|
||||
expected_submessage_content = dict(
|
||||
widget_type="todo",
|
||||
extra_data=dict(
|
||||
task_list_title="School Work",
|
||||
),
|
||||
extra_data=dict(task_list_title="School Work", tasks=[]),
|
||||
)
|
||||
|
||||
submessage = SubMessage.objects.get(message_id=message.id)
|
||||
|
@ -283,10 +320,37 @@ class WidgetContentTestCase(ZulipTestCase):
|
|||
result = self.api_post(sender, "/api/v1/messages", payload)
|
||||
self.assert_json_success(result)
|
||||
|
||||
expected_submessage_content = dict(
|
||||
widget_type="todo",
|
||||
extra_data=dict(task_list_title="", tasks=[]),
|
||||
)
|
||||
|
||||
message = self.get_last_message()
|
||||
self.assertEqual(message.content, content)
|
||||
submessage = SubMessage.objects.get(message_id=message.id)
|
||||
self.assertEqual(submessage.msg_type, "widget")
|
||||
self.assertEqual(orjson.loads(submessage.content), expected_submessage_content)
|
||||
# Now supply both task list title and tasks.
|
||||
|
||||
content = "/todo School Work\nchemistry homework: assignment 2\nstudy for english test: pages 56-67"
|
||||
payload["content"] = content
|
||||
result = self.api_post(sender, "/api/v1/messages", payload)
|
||||
self.assert_json_success(result)
|
||||
|
||||
expected_submessage_content = dict(
|
||||
widget_type="todo",
|
||||
extra_data=dict(
|
||||
task_list_title="",
|
||||
task_list_title="School Work",
|
||||
tasks=[
|
||||
dict(
|
||||
task="chemistry homework",
|
||||
desc="assignment 2",
|
||||
),
|
||||
dict(
|
||||
task="study for english test",
|
||||
desc="pages 56-67",
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
|
||||
|
|
Loading…
Reference in New Issue