refactor: enhance TimerPage layout and functionality; update theme and tokens for improved design consistency
- Refactored TimerPage component to improve layout and user experience. - Updated theme configuration to utilize design tokens for better maintainability. - Introduced new tokens for colors and typography to align with design principles. - Simplified state management and improved timer functionality. - Enhanced accessibility and responsiveness of UI elements.
This commit is contained in:
84 files changed
+4501
-2682
No files matched your search
@@ -63,8 +63,5 @@ class TodoSource(Protocol):
|
||||
def fetch(self) -> list[TodoItem]:
|
||||
"""Every open todo this source knows about."""
|
||||
|
||||
def complete(self, item: TodoItem) -> bool:
|
||||
"""Close the task in the external system. False = not supported."""
|
||||
|
||||
def check(self) -> str:
|
||||
"""Verify credentials; return a short human description of the account."""
|
||||
@@ -142,25 +142,6 @@ class GoogleTasksSource:
|
||||
def check(self) -> str:
|
||||
return f"{len(self._task_lists())} Google task list(s) visible"
|
||||
|
||||
def complete(self, item: TodoItem) -> bool:
|
||||
"""PATCH the task's status — Google Tasks has no dedicated close verb."""
|
||||
if not self._config.writeback_done:
|
||||
return False
|
||||
list_id, _, task_id = item.external_id.partition(":")
|
||||
if not (list_id and task_id):
|
||||
raise SourceError(f"Unusable Google task id: {item.external_id!r}")
|
||||
try:
|
||||
resp = self._client.patch(
|
||||
f"{_API_BASE}/lists/{list_id}/tasks/{task_id}",
|
||||
json={"status": "completed"},
|
||||
headers={"Authorization": f"Bearer {self._token()}"},
|
||||
)
|
||||
except httpx.HTTPError as exc:
|
||||
raise SourceError(f"Google is unreachable: {exc}") from exc
|
||||
if not resp.is_success:
|
||||
raise SourceError(f"Google could not complete the task: {resp.status_code}")
|
||||
return True
|
||||
|
||||
def close(self) -> None:
|
||||
self._client.close()
|
||||
|
||||
|
||||
@@ -6,8 +6,8 @@ over from jira-helper's client. The difference is the query: jira-helper asks
|
||||
"what is due today", this asks "what is open at all", because you need
|
||||
something to start a pomodoro on even when nothing is due.
|
||||
|
||||
Writing back is new here: finishing a Jira-backed pomodoro task transitions the
|
||||
issue to Done, so the app is not a second place to keep your task list in sync.
|
||||
Nothing is written back: you close an issue in Jira, and the next refresh
|
||||
notices it has left the "open issues" query. One owner per task.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
@@ -110,45 +110,6 @@ class JiraSource:
|
||||
me = self._request("GET", "/rest/api/3/myself") or {}
|
||||
return f"{me.get('displayName', 'unknown')} on {self._base_url}"
|
||||
|
||||
def complete(self, item: TodoItem) -> bool:
|
||||
"""Transition the issue into a Done-category status.
|
||||
|
||||
Jira has no generic "close" verb: every project defines its own workflow,
|
||||
so we ask the issue which transitions it offers and pick the one named in
|
||||
the settings, else any transition landing in the Done category.
|
||||
"""
|
||||
if not self._config.writeback_done:
|
||||
return False
|
||||
key = item.external_id
|
||||
data = self._request("GET", f"/rest/api/3/issue/{key}/transitions") or {}
|
||||
transitions = data.get("transitions", [])
|
||||
wanted = self._config.done_transition_name.strip().lower()
|
||||
|
||||
chosen = next(
|
||||
(t for t in transitions if (t.get("name") or "").lower() == wanted),
|
||||
None,
|
||||
) or next(
|
||||
(
|
||||
t
|
||||
for t in transitions
|
||||
if ((t.get("to") or {}).get("statusCategory") or {}).get("key") == "done"
|
||||
),
|
||||
None,
|
||||
)
|
||||
if chosen is None:
|
||||
raise SourceError(
|
||||
f"{key} has no transition to a Done status"
|
||||
+ (f" named {self._config.done_transition_name!r}" if wanted else "")
|
||||
+ ". Available: "
|
||||
+ ", ".join(t.get("name", "?") for t in transitions)
|
||||
)
|
||||
self._request(
|
||||
"POST",
|
||||
f"/rest/api/3/issue/{key}/transitions",
|
||||
json={"transition": {"id": chosen["id"]}},
|
||||
)
|
||||
return True
|
||||
|
||||
def close(self) -> None:
|
||||
self._client.close()
|
||||
|
||||
|
||||
@@ -133,9 +133,6 @@ class LaundrySource:
|
||||
self._check_session()
|
||||
return f"signed in at {self._base_url}"
|
||||
|
||||
def complete(self, item: TodoItem) -> bool:
|
||||
return False # the washing machine finishes on its own
|
||||
|
||||
def close(self) -> None:
|
||||
self._client.close()
|
||||
|
||||
|
||||
@@ -93,21 +93,6 @@ def fetch_all(settings: AppSettings) -> list[SourceResult]:
|
||||
_close(source)
|
||||
|
||||
|
||||
def complete_remote(settings: AppSettings, item: TodoItem) -> tuple[bool, str | None]:
|
||||
"""Close a task in its own system. Returns `(did_write, error)`."""
|
||||
sources = build_sources(settings)
|
||||
source = sources.get(item.source)
|
||||
if source is None:
|
||||
return False, None
|
||||
try:
|
||||
return bool(source.complete(item)), None
|
||||
except SourceError as exc:
|
||||
return False, str(exc)
|
||||
finally:
|
||||
for built in sources.values():
|
||||
_close(built)
|
||||
|
||||
|
||||
def check_source(settings: AppSettings, name: str) -> tuple[bool, str]:
|
||||
"""Verify one source's credentials — what the settings page's Test button calls."""
|
||||
sources = build_sources(settings)
|
||||
|
||||
@@ -117,18 +117,6 @@ class TodoistSource:
|
||||
projects = self._projects()
|
||||
return f"{len(projects)} Todoist project(s) visible"
|
||||
|
||||
def complete(self, item: TodoItem) -> bool:
|
||||
"""`POST /tasks/{id}/close` — the same verb the apps' checkbox uses."""
|
||||
if not self._config.writeback_done:
|
||||
return False
|
||||
try:
|
||||
resp = self._client.post(f"/tasks/{item.external_id}/close")
|
||||
except httpx.HTTPError as exc:
|
||||
raise SourceError(f"Todoist is unreachable: {exc}") from exc
|
||||
if not resp.is_success:
|
||||
raise SourceError(f"Todoist could not close the task: {resp.status_code}")
|
||||
return True
|
||||
|
||||
def close(self) -> None:
|
||||
self._client.close()
|
||||
|
||||
|
||||
Reference in new issue
Block a user