mirror of
https://github.com/leon-ai/leon.git
synced 2024-12-25 01:31:47 +03:00
feat: new onFetch
skill API and more
This commit is contained in:
parent
9aa9e0ba49
commit
8bca0595b4
@ -128,7 +128,7 @@ export default class Chatbot {
|
||||
}
|
||||
|
||||
const data = await axios.get(
|
||||
`${this.serverURL}/api/v1/fetch-widget?skill_action=${widgetContainer.onFetchAction}&widget_id=${widgetContainer.widgetId}`
|
||||
`${this.serverURL}/api/v1/fetch-widget?skill_action=${widgetContainer.onFetch.actionName}&widget_id=${widgetContainer.widgetId}`
|
||||
)
|
||||
const fetchedWidget = data.data.widget
|
||||
const reactNode = fetchedWidget
|
||||
@ -201,7 +201,7 @@ export default class Chatbot {
|
||||
/**
|
||||
* On widget fetching, render the loader
|
||||
*/
|
||||
if (isCreatingFromLoadingFeed && parsedWidget.onFetchAction) {
|
||||
if (isCreatingFromLoadingFeed && parsedWidget.onFetch) {
|
||||
const root = createRoot(container)
|
||||
|
||||
root.render(
|
||||
@ -217,7 +217,7 @@ export default class Chatbot {
|
||||
WIDGETS_TO_FETCH.push({
|
||||
reactRootNode: root,
|
||||
widgetId: parsedWidget.id,
|
||||
onFetchAction: parsedWidget.onFetchAction
|
||||
onFetch: parsedWidget.onFetch
|
||||
})
|
||||
|
||||
return
|
||||
|
@ -133,7 +133,7 @@ class Leon {
|
||||
actionName: `${INTENT_OBJECT.domain}:${INTENT_OBJECT.skill}:${INTENT_OBJECT.action}`,
|
||||
widget: answerInput.widget.widget,
|
||||
id: answerInput.widget.id,
|
||||
onFetchAction: answerInput.widget.onFetchAction,
|
||||
onFetch: answerInput.widget.onFetch ?? null,
|
||||
componentTree: new WidgetWrapper({
|
||||
...answerInput.widget.wrapperProps,
|
||||
children: [answerInput.widget.render()]
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { INTENT_OBJECT } from '@bridge/constants'
|
||||
|
||||
/**
|
||||
* Get the widget ID if any
|
||||
* Get the widget id if any
|
||||
* @example getWidgetId() // 'timerwidget-5q1xlzeh
|
||||
*/
|
||||
export function getWidgetId(): string | null {
|
||||
|
@ -5,11 +5,6 @@ import { WidgetComponent } from '@sdk/widget-component'
|
||||
|
||||
type UtteranceSender = 'leon' | 'owner'
|
||||
|
||||
interface FetchWidgetDataWidgetEventMethodParams {
|
||||
actionName: string
|
||||
widgetId: string
|
||||
dataToSet: string[]
|
||||
}
|
||||
interface SendUtteranceWidgetEventMethodParams {
|
||||
from: UtteranceSender
|
||||
utterance: string
|
||||
@ -28,11 +23,13 @@ export interface WidgetEventMethod {
|
||||
methodParams:
|
||||
| SendUtteranceWidgetEventMethodParams
|
||||
| RunSkillActionWidgetEventMethodParams
|
||||
| FetchWidgetDataWidgetEventMethodParams
|
||||
}
|
||||
export interface WidgetOptions<T = unknown> {
|
||||
wrapperProps?: Omit<WidgetWrapperProps, 'children'>
|
||||
onFetchAction?: string
|
||||
onFetch?: {
|
||||
widgetId?: string | undefined
|
||||
actionName: string
|
||||
}
|
||||
params: T
|
||||
}
|
||||
|
||||
@ -40,7 +37,7 @@ export abstract class Widget<T = unknown> {
|
||||
public actionName: string
|
||||
public id: string
|
||||
public widget: string
|
||||
public onFetchAction: string | null = null
|
||||
public onFetch: WidgetOptions<T>['onFetch'] | null = null
|
||||
public wrapperProps: WidgetOptions<T>['wrapperProps']
|
||||
public params: WidgetOptions<T>['params']
|
||||
|
||||
@ -48,15 +45,20 @@ export abstract class Widget<T = unknown> {
|
||||
if (options?.wrapperProps) {
|
||||
this.wrapperProps = options.wrapperProps
|
||||
}
|
||||
if (options?.onFetchAction) {
|
||||
this.onFetchAction = `${INTENT_OBJECT.domain}:${INTENT_OBJECT.skill}:${options.onFetchAction}`
|
||||
}
|
||||
this.actionName = `${INTENT_OBJECT.domain}:${INTENT_OBJECT.skill}:${INTENT_OBJECT.action}`
|
||||
this.widget = this.constructor.name
|
||||
this.id = `${this.widget.toLowerCase()}-${Math.random()
|
||||
.toString(36)
|
||||
.substring(2, 10)}`
|
||||
this.params = options.params
|
||||
this.widget = this.constructor.name
|
||||
if (options?.onFetch) {
|
||||
this.onFetch = {
|
||||
widgetId: options.onFetch.widgetId,
|
||||
actionName: `${INTENT_OBJECT.domain}:${INTENT_OBJECT.skill}:${options.onFetch.actionName}`
|
||||
}
|
||||
}
|
||||
this.id =
|
||||
options.onFetch?.widgetId ||
|
||||
`${this.widget.toLowerCase()}-${Math.random()
|
||||
.toString(36)
|
||||
.substring(2, 10)}`
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -84,12 +84,12 @@ class Leon:
|
||||
'actionName': f"{INTENT_OBJECT['domain']}:{INTENT_OBJECT['skill']}:{INTENT_OBJECT['action']}",
|
||||
'widget': widget.widget,
|
||||
'id': widget.id,
|
||||
'onFetchAction': widget.on_fetch_action,
|
||||
'onFetch': widget.on_fetch if hasattr(widget, 'on_fetch') else None,
|
||||
'componentTree': WidgetWrapper({
|
||||
**wrapper_props,
|
||||
'children': [widget.render()]
|
||||
}).__dict__(),
|
||||
'supportedEvents': SUPPORTED_WIDGET_EVENTS # You might need to define this constant
|
||||
'supportedEvents': SUPPORTED_WIDGET_EVENTS
|
||||
}
|
||||
|
||||
answer_object = {
|
||||
|
@ -2,6 +2,7 @@ from typing import Any, Optional, Generic, TypeVar, Literal, TypedDict, Union, D
|
||||
from dataclasses import dataclass
|
||||
from abc import ABC, abstractmethod
|
||||
import random
|
||||
import string
|
||||
|
||||
from .widget_component import WidgetComponent
|
||||
from ..constants import SKILL_CONFIG, INTENT_OBJECT
|
||||
@ -11,12 +12,6 @@ T = TypeVar('T')
|
||||
UtteranceSender = Literal['leon', 'owner']
|
||||
|
||||
|
||||
class FetchWidgetDataWidgetEventMethodParams(TypedDict):
|
||||
action_name: str
|
||||
widget_id: str
|
||||
data_to_set: list[str]
|
||||
|
||||
|
||||
class SendUtteranceWidgetEventMethodParams(TypedDict):
|
||||
from_: UtteranceSender
|
||||
utterance: str
|
||||
@ -36,8 +31,7 @@ class WidgetEventMethod(TypedDict):
|
||||
methodName: Literal['send_utterance', 'run_skill_action']
|
||||
methodParams: Union[
|
||||
SendUtteranceWidgetEventMethodParams,
|
||||
RunSkillActionWidgetEventMethodParams,
|
||||
FetchWidgetDataWidgetEventMethodParams
|
||||
RunSkillActionWidgetEventMethodParams
|
||||
]
|
||||
|
||||
|
||||
@ -45,18 +39,23 @@ class WidgetEventMethod(TypedDict):
|
||||
class WidgetOptions(Generic[T]):
|
||||
wrapper_props: dict[str, Any] = None
|
||||
params: T = None
|
||||
on_fetch_action: Optional[str] = None
|
||||
on_fetch: Optional[dict[str, Any]] = None
|
||||
|
||||
|
||||
class Widget(ABC, Generic[T]):
|
||||
def __init__(self, options: WidgetOptions[T]):
|
||||
if options.wrapper_props:
|
||||
self.wrapper_props = options.wrapper_props
|
||||
self.action_name = f"{INTENT_OBJECT['domain']}:{INTENT_OBJECT['skill']}:{INTENT_OBJECT['action']}"
|
||||
self.widget = self.__class__.__name__
|
||||
self.id = f"{self.widget.lower()}-{random.randint(100000, 999999)}"
|
||||
self.wrapper_props = options.wrapper_props
|
||||
self.params = options.params
|
||||
self.on_fetch_action = f"{INTENT_OBJECT['domain']}:{INTENT_OBJECT['skill']}:{options.on_fetch_action}" \
|
||||
if options.on_fetch_action else None
|
||||
self.widget = self.__class__.__name__
|
||||
if options.on_fetch:
|
||||
self.on_fetch = {
|
||||
'widgetId': options.on_fetch.get('widget_id'),
|
||||
'actionName': f"{INTENT_OBJECT['domain']}:{INTENT_OBJECT['skill']}:{options.on_fetch.get('action_name')}"
|
||||
}
|
||||
self.id = options.on_fetch.get('widget_id') if options.on_fetch \
|
||||
else f"{self.widget.lower()}-{''.join(random.choices(string.ascii_lowercase + string.digits, k=8))}"
|
||||
|
||||
@abstractmethod
|
||||
def render(self) -> WidgetComponent:
|
||||
|
@ -1,4 +1,6 @@
|
||||
from typing import TypeVar, Generic, TypedDict, List, Any
|
||||
import random
|
||||
import string
|
||||
|
||||
T = TypeVar('T')
|
||||
|
||||
@ -12,8 +14,6 @@ SUPPORTED_WIDGET_EVENTS = [
|
||||
|
||||
|
||||
def generate_id() -> str:
|
||||
import random
|
||||
import string
|
||||
return ''.join(random.choices(string.ascii_lowercase + string.digits, k=5))
|
||||
|
||||
|
||||
|
@ -52,64 +52,6 @@
|
||||
"route": "/api/action/games/rochambeau/rematch",
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"method": "POST",
|
||||
"route": "/api/action/news/github_trends/run",
|
||||
"params": ["number", "daterange"],
|
||||
"entitiesType": "builtIn"
|
||||
},
|
||||
{
|
||||
"method": "GET",
|
||||
"route": "/api/action/news/product_hunt_trends/run",
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"method": "POST",
|
||||
"route": "/api/action/productivity/todo_list/create_list",
|
||||
"params": ["list"],
|
||||
"entitiesType": "trim"
|
||||
},
|
||||
{
|
||||
"method": "GET",
|
||||
"route": "/api/action/productivity/todo_list/view_lists",
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"method": "POST",
|
||||
"route": "/api/action/productivity/todo_list/view_list",
|
||||
"params": ["list"],
|
||||
"entitiesType": "trim"
|
||||
},
|
||||
{
|
||||
"method": "POST",
|
||||
"route": "/api/action/productivity/todo_list/rename_list",
|
||||
"params": ["old_list", "new_list"],
|
||||
"entitiesType": "trim"
|
||||
},
|
||||
{
|
||||
"method": "POST",
|
||||
"route": "/api/action/productivity/todo_list/delete_list",
|
||||
"params": ["list"],
|
||||
"entitiesType": "trim"
|
||||
},
|
||||
{
|
||||
"method": "POST",
|
||||
"route": "/api/action/productivity/todo_list/add_todos",
|
||||
"params": ["todos", "list"],
|
||||
"entitiesType": "trim"
|
||||
},
|
||||
{
|
||||
"method": "POST",
|
||||
"route": "/api/action/productivity/todo_list/complete_todos",
|
||||
"params": ["todos", "list"],
|
||||
"entitiesType": "trim"
|
||||
},
|
||||
{
|
||||
"method": "POST",
|
||||
"route": "/api/action/productivity/todo_list/uncheck_todos",
|
||||
"params": ["todos", "list"],
|
||||
"entitiesType": "trim"
|
||||
},
|
||||
{
|
||||
"method": "GET",
|
||||
"route": "/api/action/leon/age/run",
|
||||
@ -185,11 +127,64 @@
|
||||
"route": "/api/action/leon/thanks/run",
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"method": "POST",
|
||||
"route": "/api/action/news/github_trends/run",
|
||||
"params": ["number", "daterange"],
|
||||
"entitiesType": "builtIn"
|
||||
},
|
||||
{
|
||||
"method": "GET",
|
||||
"route": "/api/action/unknown/widget-playground/run",
|
||||
"route": "/api/action/news/product_hunt_trends/run",
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"method": "POST",
|
||||
"route": "/api/action/productivity/todo_list/create_list",
|
||||
"params": ["list"],
|
||||
"entitiesType": "trim"
|
||||
},
|
||||
{
|
||||
"method": "GET",
|
||||
"route": "/api/action/productivity/todo_list/view_lists",
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"method": "POST",
|
||||
"route": "/api/action/productivity/todo_list/view_list",
|
||||
"params": ["list"],
|
||||
"entitiesType": "trim"
|
||||
},
|
||||
{
|
||||
"method": "POST",
|
||||
"route": "/api/action/productivity/todo_list/rename_list",
|
||||
"params": ["old_list", "new_list"],
|
||||
"entitiesType": "trim"
|
||||
},
|
||||
{
|
||||
"method": "POST",
|
||||
"route": "/api/action/productivity/todo_list/delete_list",
|
||||
"params": ["list"],
|
||||
"entitiesType": "trim"
|
||||
},
|
||||
{
|
||||
"method": "POST",
|
||||
"route": "/api/action/productivity/todo_list/add_todos",
|
||||
"params": ["todos", "list"],
|
||||
"entitiesType": "trim"
|
||||
},
|
||||
{
|
||||
"method": "POST",
|
||||
"route": "/api/action/productivity/todo_list/complete_todos",
|
||||
"params": ["todos", "list"],
|
||||
"entitiesType": "trim"
|
||||
},
|
||||
{
|
||||
"method": "POST",
|
||||
"route": "/api/action/productivity/todo_list/uncheck_todos",
|
||||
"params": ["todos", "list"],
|
||||
"entitiesType": "trim"
|
||||
},
|
||||
{
|
||||
"method": "GET",
|
||||
"route": "/api/action/social_communication/conversation/setup",
|
||||
@ -215,6 +210,11 @@
|
||||
"route": "/api/action/social_communication/mbti/quiz",
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"method": "GET",
|
||||
"route": "/api/action/unknown/widget-playground/run",
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"method": "GET",
|
||||
"route": "/api/action/utilities/date_time/current_date_time",
|
||||
|
@ -98,7 +98,10 @@ export interface SkillAnswerOutput extends IntentObject {
|
||||
id: string
|
||||
componentTree: WidgetWrapper
|
||||
supportedEvents: typeof SUPPORTED_WIDGET_EVENTS
|
||||
onFetchAction: string | null
|
||||
onFetch: {
|
||||
widgetId?: string
|
||||
actionName: string
|
||||
} | null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
from bridges.python.src.sdk.leon import leon
|
||||
from bridges.python.src.sdk.types import ActionParams
|
||||
from bridges.python.src.sdk.widget import WidgetOptions
|
||||
from ..widgets.todos_list_widget import TodosListWidget
|
||||
from ..lib import memory
|
||||
|
||||
from typing import Union
|
||||
@ -25,7 +27,11 @@ def run(params: ActionParams) -> None:
|
||||
}
|
||||
})
|
||||
|
||||
memory.create_todo_list(list_name)
|
||||
todos_list_widget = TodosListWidget(WidgetOptions())
|
||||
memory.create_todo_list(
|
||||
todos_list_widget.id,
|
||||
list_name
|
||||
)
|
||||
|
||||
leon.answer({
|
||||
'key': 'list_created',
|
||||
|
@ -1,7 +1,8 @@
|
||||
from bridges.python.src.sdk.leon import leon
|
||||
from bridges.python.src.sdk.toolbox import get_widget_id
|
||||
from bridges.python.src.sdk.types import ActionParams
|
||||
from bridges.python.src.sdk.widget import WidgetOptions
|
||||
from ..widgets.todos_list_widget import TodosListWidget, TodosListWidgetParams
|
||||
from ..widgets.todos_list_widget import TodosListWidget
|
||||
from ..lib import memory
|
||||
|
||||
from typing import Union
|
||||
@ -10,6 +11,7 @@ from typing import Union
|
||||
def run(params: ActionParams) -> None:
|
||||
"""View a to-do list"""
|
||||
|
||||
widget_id = get_widget_id()
|
||||
list_name: Union[str, None] = None
|
||||
|
||||
for item in params['entities']:
|
||||
@ -27,6 +29,7 @@ def run(params: ActionParams) -> None:
|
||||
}
|
||||
})
|
||||
|
||||
# TODO: if widget_id, get list by widget_id, otherwise get by list_name
|
||||
todos = memory.get_todo_items(list_name)
|
||||
|
||||
if len(todos) == 0:
|
||||
@ -37,10 +40,15 @@ def run(params: ActionParams) -> None:
|
||||
}
|
||||
})
|
||||
|
||||
todos_list_options: WidgetOptions[TodosListWidgetParams] = WidgetOptions(
|
||||
wrapper_props={'noPadding': True},
|
||||
params={'list_name': list_name, 'todos': todos}
|
||||
todos_list_widget = TodosListWidget(
|
||||
WidgetOptions(
|
||||
wrapper_props={'noPadding': True},
|
||||
params={'list_name': list_name, 'todos': todos},
|
||||
on_fetch={
|
||||
'widget_id': widget_id,
|
||||
'action_name': 'view_list'
|
||||
}
|
||||
)
|
||||
)
|
||||
todos_list_widget = TodosListWidget(todos_list_options)
|
||||
|
||||
leon.answer({'widget': todos_list_widget})
|
||||
|
@ -15,13 +15,14 @@ todo_items_memory = Memory({
|
||||
})
|
||||
|
||||
|
||||
class TodoList(TypedDict):
|
||||
class TodoListMemory(TypedDict):
|
||||
widget_id: str
|
||||
name: str
|
||||
created_at: str
|
||||
updated_at: str
|
||||
|
||||
|
||||
class TodoItem(TypedDict):
|
||||
class TodoItemMemory(TypedDict):
|
||||
todo_list_name: str
|
||||
name: str
|
||||
is_completed: bool
|
||||
@ -29,21 +30,22 @@ class TodoItem(TypedDict):
|
||||
updated_at: str
|
||||
|
||||
|
||||
def create_todo_list(name: str) -> None:
|
||||
def create_todo_list(widget_id: str, name: str) -> None:
|
||||
"""Create a new todo list"""
|
||||
|
||||
datetime_now = datetime.now().isoformat()
|
||||
todo_list = TodoList(
|
||||
todo_list = TodoListMemory(
|
||||
widget_id=widget_id,
|
||||
name=name,
|
||||
created_at=datetime_now,
|
||||
updated_at=datetime_now
|
||||
)
|
||||
todo_lists: list[TodoList] = todo_lists_memory.read()
|
||||
todo_lists: list[TodoListMemory] = todo_lists_memory.read()
|
||||
todo_lists.append(todo_list)
|
||||
todo_lists_memory.write(todo_lists)
|
||||
|
||||
|
||||
def get_todo_lists() -> list[TodoList]:
|
||||
def get_todo_lists() -> list[TodoListMemory]:
|
||||
"""Get all todo lists"""
|
||||
|
||||
return todo_lists_memory.read()
|
||||
@ -52,7 +54,7 @@ def get_todo_lists() -> list[TodoList]:
|
||||
def update_todo_list(old_name: str, new_name: str) -> None:
|
||||
"""Update a todo list name"""
|
||||
|
||||
todo_lists: list[TodoList] = todo_lists_memory.read()
|
||||
todo_lists: list[TodoListMemory] = todo_lists_memory.read()
|
||||
for todo_list in todo_lists:
|
||||
if todo_list['name'] == old_name:
|
||||
todo_list['name'] = new_name
|
||||
@ -60,7 +62,7 @@ def update_todo_list(old_name: str, new_name: str) -> None:
|
||||
break
|
||||
todo_lists_memory.write(todo_lists)
|
||||
|
||||
todo_items: list[TodoItem] = todo_items_memory.read()
|
||||
todo_items: list[TodoItemMemory] = todo_items_memory.read()
|
||||
for todo_item in todo_items:
|
||||
if todo_item['todo_list_name'] == old_name:
|
||||
todo_item['todo_list_name'] = new_name
|
||||
@ -71,14 +73,14 @@ def update_todo_list(old_name: str, new_name: str) -> None:
|
||||
def delete_todo_list(name: str) -> None:
|
||||
"""Delete a todo list and its todos"""
|
||||
|
||||
todo_lists: list[TodoList] = todo_lists_memory.read()
|
||||
todo_lists: list[TodoListMemory] = todo_lists_memory.read()
|
||||
for todo_list in todo_lists:
|
||||
if todo_list['name'] == name:
|
||||
todo_lists.remove(todo_list)
|
||||
break
|
||||
todo_lists_memory.write(todo_lists)
|
||||
|
||||
todo_items: list[TodoItem] = todo_items_memory.read()
|
||||
todo_items: list[TodoItemMemory] = todo_items_memory.read()
|
||||
for todo_item in todo_items:
|
||||
if todo_item['todo_list_name'] == name:
|
||||
todo_items.remove(todo_item)
|
||||
@ -94,7 +96,7 @@ def count_todo_lists() -> int:
|
||||
def has_todo_list(name: str) -> bool:
|
||||
"""Check if a todo list already exist"""
|
||||
|
||||
todo_lists: list[TodoList] = todo_lists_memory.read()
|
||||
todo_lists: list[TodoListMemory] = todo_lists_memory.read()
|
||||
for todo_list in todo_lists:
|
||||
if todo_list['name'] == name:
|
||||
return True
|
||||
@ -107,22 +109,22 @@ def create_todo_item(todo_list_name: str, name: str) -> None:
|
||||
if not has_todo_list(todo_list_name):
|
||||
create_todo_list(todo_list_name)
|
||||
datetime_now = datetime.now().isoformat()
|
||||
todo_item = TodoItem(
|
||||
todo_item = TodoItemMemory(
|
||||
todo_list_name=todo_list_name,
|
||||
name=name,
|
||||
is_completed=False,
|
||||
created_at=datetime_now,
|
||||
updated_at=datetime_now
|
||||
)
|
||||
todo_items: list[TodoItem] = todo_items_memory.read()
|
||||
todo_items: list[TodoItemMemory] = todo_items_memory.read()
|
||||
todo_items.append(todo_item)
|
||||
todo_items_memory.write(todo_items)
|
||||
|
||||
|
||||
def get_todo_items(todo_list_name: str) -> list[TodoItem]:
|
||||
def get_todo_items(todo_list_name: str) -> list[TodoItemMemory]:
|
||||
"""Get all todo items of a todo list"""
|
||||
|
||||
todo_items: list[TodoItem] = todo_items_memory.read()
|
||||
todo_items: list[TodoItemMemory] = todo_items_memory.read()
|
||||
return [todo_item for todo_item in todo_items if todo_item['todo_list_name'] == todo_list_name]
|
||||
|
||||
|
||||
@ -132,24 +134,24 @@ def count_todo_items(todo_list_name: str) -> int:
|
||||
return len(get_todo_items(todo_list_name))
|
||||
|
||||
|
||||
def get_completed_todo_items(todo_list_name: str) -> list[TodoItem]:
|
||||
def get_completed_todo_items(todo_list_name: str) -> list[TodoItemMemory]:
|
||||
"""Get all completed todo items of a todo list"""
|
||||
|
||||
todo_items: list[TodoItem] = todo_items_memory.read()
|
||||
todo_items: list[TodoItemMemory] = todo_items_memory.read()
|
||||
return [todo_item for todo_item in todo_items if todo_item['todo_list_name'] == todo_list_name and todo_item['is_completed']]
|
||||
|
||||
|
||||
def get_uncompleted_todo_items(todo_list_name: str) -> list[TodoItem]:
|
||||
def get_uncompleted_todo_items(todo_list_name: str) -> list[TodoItemMemory]:
|
||||
"""Get all uncompleted todo items of a todo list"""
|
||||
|
||||
todo_items: list[TodoItem] = todo_items_memory.read()
|
||||
todo_items: list[TodoItemMemory] = todo_items_memory.read()
|
||||
return [todo_item for todo_item in todo_items if todo_item['todo_list_name'] == todo_list_name and not todo_item['is_completed']]
|
||||
|
||||
|
||||
def complete_todo_item(todo_list_name: str, name: str) -> None:
|
||||
"""Complete a todo item"""
|
||||
|
||||
todo_items: list[TodoItem] = todo_items_memory.read()
|
||||
todo_items: list[TodoItemMemory] = todo_items_memory.read()
|
||||
for todo_item in todo_items:
|
||||
if todo_item['todo_list_name'] == todo_list_name and todo_item['name'] == name:
|
||||
todo_item['is_completed'] = True
|
||||
@ -161,7 +163,7 @@ def complete_todo_item(todo_list_name: str, name: str) -> None:
|
||||
def uncomplete_todo_item(todo_list_name: str, name: str) -> None:
|
||||
"""Uncomplete a todo item"""
|
||||
|
||||
todo_items: list[TodoItem] = todo_items_memory.read()
|
||||
todo_items: list[TodoItemMemory] = todo_items_memory.read()
|
||||
for todo_item in todo_items:
|
||||
if todo_item['todo_list_name'] == todo_list_name and todo_item['name'] == name:
|
||||
todo_item['is_completed'] = False
|
||||
|
@ -14,6 +14,7 @@ class TodoType(TypedDict):
|
||||
|
||||
|
||||
class TodosListWidgetParams(TypedDict):
|
||||
id: str
|
||||
list_name: str
|
||||
todos: list[TodoType]
|
||||
|
||||
|
@ -24,13 +24,15 @@ export const run: ActionFunction = async function () {
|
||||
|
||||
const timerWidget = new TimerWidget({
|
||||
params: {
|
||||
id: widgetId ?? timerMemory.widgetId,
|
||||
seconds: remainingTime,
|
||||
initialProgress,
|
||||
initialDuration: duration,
|
||||
interval
|
||||
},
|
||||
onFetchAction: 'check_timer'
|
||||
onFetch: {
|
||||
widgetId: widgetId ?? timerMemory.widgetId,
|
||||
actionName: 'check_timer'
|
||||
}
|
||||
})
|
||||
|
||||
await leon.answer({ widget: timerWidget })
|
||||
|
@ -29,18 +29,16 @@ export const run: ActionFunction = async function (params) {
|
||||
initialProgress: 0,
|
||||
interval
|
||||
},
|
||||
onFetchAction: 'check_timer'
|
||||
onFetch: {
|
||||
actionName: 'check_timer'
|
||||
}
|
||||
})
|
||||
|
||||
await createTimerMemory(timerWidget.id, seconds, interval)
|
||||
|
||||
// TODO: return a speech without new utterance
|
||||
/*await leon.answer({
|
||||
widget: timerWidget,
|
||||
speech: 'I set a timer for ... ...'
|
||||
})*/
|
||||
await leon.answer({
|
||||
widget: timerWidget,
|
||||
key: 'timer_set'
|
||||
})
|
||||
await Promise.all([
|
||||
createTimerMemory(timerWidget.id, seconds, interval),
|
||||
leon.answer({
|
||||
widget: timerWidget,
|
||||
key: 'timer_set'
|
||||
})
|
||||
])
|
||||
}
|
||||
|
@ -8,16 +8,11 @@ interface Params {
|
||||
interval: number
|
||||
initialProgress: number
|
||||
initialDuration?: number
|
||||
id?: string
|
||||
}
|
||||
|
||||
export class TimerWidget extends Widget<Params> {
|
||||
constructor(options: WidgetOptions<Params>) {
|
||||
super(options)
|
||||
|
||||
if (options.params.id) {
|
||||
this.id = options.params.id
|
||||
}
|
||||
}
|
||||
|
||||
public render(): WidgetComponent {
|
||||
|
Loading…
Reference in New Issue
Block a user