Skip to content

Commit

Permalink
fix(notifications): notifications not getting closed nor updated
Browse files Browse the repository at this point in the history
sassanh committed Aug 25, 2024

Verified

This commit was signed with the committer’s verified signature.
sassanh Sassan Haradji
1 parent 09713b1 commit 158dc51
Showing 10 changed files with 134 additions and 100 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## Version 0.15.11

- fix(notifications): notifications not getting closed nor updated

## Version 0.15.10

- refactor(core): use `dpkg-query` instead of `apt` python api as loading `Cache` in `apt` is slow and use it in docker service
46 changes: 23 additions & 23 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "ubo-app"
version = "0.15.10"
version = "0.15.11"
description = "Ubo main app, running on device initialization. A platform for running other apps."
authors = ["Sassan Haradji <[email protected]>"]
license = "Apache-2.0"
@@ -61,13 +61,13 @@ optional = true

[tool.poetry.group.dev.dependencies]
poethepoet = "^0.24.4"
pyright = "^1.1.376"
pyright = "^1.1.377"
pytest = "^8.0.0"
pytest-asyncio = "^0.23.5.post1"
pytest-cov = "^4.1.0"
pytest-timeout = "^2.3.1"
pytest-xdist = "^3.5.0"
ruff = "^0.6.0"
ruff = "^0.6.2"
tenacity = "^8.2.3"
toml = "^0.10.2"
pytest-mock = "^3.14.0"
4 changes: 2 additions & 2 deletions ubo_app/menu_app/menu_central.py
Original file line number Diff line number Diff line change
@@ -71,11 +71,11 @@ def __init__(self: MenuAppCentral, **kwargs: object) -> None:
super().__init__(**kwargs)
self.menu_widget = MenuWidgetWithHomePage(render_surroundings=True)

_self = weakref.ref(self)

self.menu_widget.bind(page_index=self.handle_page_index_change)
self.menu_widget.bind(current_menu=self.handle_page_index_change)

_self = weakref.ref(self)

@autorun(lambda state: state.main.menu)
@debounce(0.1, DebounceOptions(leading=True, trailing=True, time_window=0.1))
@mainthread
126 changes: 75 additions & 51 deletions ubo_app/menu_app/menu_notification_handler.py
Original file line number Diff line number Diff line change
@@ -2,6 +2,7 @@
from __future__ import annotations

import functools
import weakref
from dataclasses import replace
from typing import TYPE_CHECKING

@@ -31,6 +32,12 @@
from ubo_gui.menu.menu_widget import MenuWidget


class NotificationReference:
def __init__(self: NotificationReference, notification: Notification) -> None:
self.value = notification
self.is_initialized = False


class MenuNotificationHandler(UboApp):
menu_widget: MenuWidget

@@ -47,12 +54,15 @@ def display_notification( # noqa: C901
and stack_item.application.notification_id == event.notification.id
for stack_item in self.menu_widget.stack
)
) or event.notification.display_type is NotificationDisplayType.BACKGROUND:
) or (
event.notification.display_type is NotificationDisplayType.BACKGROUND
and event.index is None
):
return

subscriptions = []

notification = event.notification
notification = NotificationReference(event.notification)
is_closed = False

@mainthread
@@ -65,65 +75,85 @@ def close(_: object = None) -> None:
unsubscribe()
notification_application.unbind(on_close=close)
dispatch(CloseApplicationEvent(application=notification_application))
if notification.dismiss_on_close:
dispatch(NotificationsClearAction(notification=notification))
if notification.on_close:
notification.on_close()

notification_application = NotificationWidget(
notification_title=notification.title,
content=notification.content,
icon=notification.icon,
color=notification.color,
items=self._notification_items(notification, close),
title=f'Notification ({event.index + 1}/{event.count})'
if event.index is not None
else ' ',
)
notification_application.notification_id = notification.id

dispatch(OpenApplicationEvent(application=notification_application))
if notification.value.dismiss_on_close:
dispatch(NotificationsClearAction(notification=notification.value))
if notification.value.on_close:
notification.value.on_close()

if notification.display_type is NotificationDisplayType.FLASH:
Clock.schedule_once(close, notification.flash_time)

notification_application.bind(on_close=close)

@mainthread
def clear_notification(event: NotificationsClearEvent) -> None:
if event.notification == notification:
close()

_self = weakref.ref(self)

def renew_notification(event: NotificationsDisplayEvent) -> None:
nonlocal notification
if event.notification.id == notification.id:
notification = event.notification
self._update_notification_widget(notification_application, event, close)
self = _self()
if self is None:
return
if event.notification.id == notification.value.id:
notification.value = event.notification
self._update_notification_widget(
notification_application,
event,
notification,
close,
)

if event.notification.extra_information and (
not notification.is_initialized
or event.notification.id is None
or event.notification.id != notification.value.id
or not notification.value.extra_information
or event.notification.extra_information
!= notification.value.extra_information
):
notification.is_initialized = True
dispatch(
VoiceReadTextAction(
text=event.notification.extra_information.text,
piper_text=event.notification.extra_information.piper_text,
picovoice_text=event.notification.extra_information.picovoice_text,
),
)

notification_application = NotificationWidget(items=[None] * PAGE_MAX_ITEMS)
notification_application.notification_id = notification.value.id

if (
notification.value.display_type is NotificationDisplayType.FLASH
and event.index is None
):
Clock.schedule_once(close, notification.value.flash_time)

notification_application.bind(on_close=close)

subscriptions.append(
subscribe_event(
NotificationsClearEvent,
clear_notification,
),
)
if notification.id is not None:
if notification.value.id is not None:
subscriptions.append(
subscribe_event(
NotificationsDisplayEvent,
renew_notification,
keep_ref=False,
),
)

renew_notification(event)

dispatch(OpenApplicationEvent(application=notification_application))

def _notification_items(
self: MenuNotificationHandler,
notification: Notification,
notification: NotificationReference,
close: Callable[[], None],
) -> list[NotificationActionItem | None]:
def dismiss(_: object = None) -> None:
close()
if not notification.dismiss_on_close:
dispatch(NotificationsClearAction(notification=notification))
if not notification.value.dismiss_on_close:
dispatch(NotificationsClearAction(notification=notification.value))

def run_notification_action(action: NotificationActionItem) -> None:
result = action.action()
@@ -135,15 +165,8 @@ def run_notification_action(action: NotificationActionItem) -> None:

items: list[NotificationActionItem | None] = []

if notification.extra_information:
text = notification.extra_information.text
dispatch(
VoiceReadTextAction(
text=notification.extra_information.text,
piper_text=notification.extra_information.piper_text,
picovoice_text=notification.extra_information.picovoice_text,
),
)
if notification.value.extra_information:
text = notification.value.extra_information.text

def open_info() -> None:
info_application = NotificationInfo(text=text)
@@ -166,10 +189,10 @@ def open_info() -> None:
is_short=True,
action=functools.partial(run_notification_action, action),
)
for action in notification.actions
for action in notification.value.actions
]

if notification.dismissable:
if notification.value.dismissable:
items.append(
NotificationActionItem(
icon='󰆴',
@@ -187,14 +210,15 @@ def _update_notification_widget(
self: MenuNotificationHandler,
notification_application: NotificationWidget,
event: NotificationsDisplayEvent,
notification: NotificationReference,
close: Callable[[], None],
) -> None:
notification_application.notification_title = event.notification.title
notification_application.content = event.notification.content
notification_application.icon = event.notification.icon
notification_application.color = event.notification.color
notification_application.notification_title = notification.value.title
notification_application.content = notification.value.content
notification_application.icon = notification.value.icon
notification_application.color = notification.value.color
notification_application.items = self._notification_items(
event.notification,
notification,
close,
)
notification_application.title = (
11 changes: 9 additions & 2 deletions ubo_app/services/010-notifications/reducer.py
Original file line number Diff line number Diff line change
@@ -7,6 +7,7 @@
from redux import (
BaseEvent,
CompleteReducerResult,
FinishAction,
InitAction,
InitializationActionError,
ReducerResult,
@@ -148,6 +149,12 @@ def reducer(
for notification in to_be_removed
],
)
if isinstance(action, NotificationsClearAllAction):
return replace(state, notifications=[], unread_count=0)
if isinstance(action, NotificationsClearAllAction | FinishAction):
return CompleteReducerResult(
state=replace(state, notifications=[], unread_count=0),
events=[
NotificationsClearEvent(notification=notification)
for notification in state.notifications
],
)
return state
12 changes: 2 additions & 10 deletions ubo_app/store/core/_menus.py
Original file line number Diff line number Diff line change
@@ -2,7 +2,6 @@

import functools
import socket
from dataclasses import replace
from datetime import UTC, datetime
from typing import TYPE_CHECKING

@@ -21,11 +20,7 @@
SettingsCategory,
)
from ubo_app.store.main import autorun, dispatch
from ubo_app.store.services.notifications import (
Notification,
NotificationDisplayType,
NotificationsDisplayEvent,
)
from ubo_app.store.services.notifications import Notification, NotificationsDisplayEvent
from ubo_app.store.update_manager.utils import (
BASE_IMAGE,
CURRENT_VERSION,
@@ -116,10 +111,7 @@ def notifications_menu_items(notifications: Sequence[Notification]) -> list[Item
action=functools.partial(
dispatch,
NotificationsDisplayEvent(
notification=replace(
notification,
display_type=NotificationDisplayType.STICKY,
),
notification=notification,
index=index,
count=len(notifications),
),
1 change: 1 addition & 0 deletions ubo_app/store/update_manager/__init__.py
Original file line number Diff line number Diff line change
@@ -7,6 +7,7 @@
from redux import BaseAction, BaseEvent

UPDATE_MANAGER_NOTIFICATION_ID = 'ubo:update_manager'
UPDATE_MANAGER_SECOND_PHASE_NOTIFICATION_ID = 'ubo:update_manager:phase-2'


class UpdateManagerAction(BaseAction): ...
2 changes: 1 addition & 1 deletion ubo_app/store/update_manager/reducer.py
Original file line number Diff line number Diff line change
@@ -77,7 +77,7 @@ def reducer(
id=UPDATE_MANAGER_NOTIFICATION_ID,
title='Update available!',
content=f"""Ubo v{action.latest_version} is available. Go to
the About menu to update.""",
the About menu to update.""",
display_type=NotificationDisplayType.FLASH
if action.flash_notification
else NotificationDisplayType.BACKGROUND,
22 changes: 14 additions & 8 deletions ubo_app/store/update_manager/utils.py
Original file line number Diff line number Diff line change
@@ -36,6 +36,7 @@
)
from ubo_app.store.update_manager import (
UPDATE_MANAGER_NOTIFICATION_ID,
UPDATE_MANAGER_SECOND_PHASE_NOTIFICATION_ID,
UpdateManagerSetStatusAction,
UpdateManagerSetVersionsAction,
UpdateManagerState,
@@ -166,6 +167,7 @@ async def download_files() -> None:
icon='󰇚',
blink=False,
progress=1 / packages_count,
dismissable=False,
),
),
)
@@ -176,18 +178,17 @@ async def download_files() -> None:
'download',
'--dest',
UPDATE_ASSETS_PATH,
'ubo-app[default]',
'setuptools',
'wheel',
'ubo-app[default]',
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
if process.stdout is None:
logger.exception('Failed to update (pip has no stdout)')
logger.info('Failed to update (pip has no stdout)')
dispatch(
NotificationsAddAction(
notification=Notification(
id=UPDATE_MANAGER_NOTIFICATION_ID,
title='Failed to update',
content='Failed to download packages',
display_type=NotificationDisplayType.FLASH,
@@ -218,18 +219,19 @@ async def download_files() -> None:
icon='󰇚',
blink=False,
progress=min((counter + 1) / packages_count, 1),
dismissable=False,
),
),
)
await process.wait()

# Update the packages count estimate for the next update
Path(packages_count_path).write_text(str(counter), encoding='utf-8')

if process.returncode != 0:
msg = 'Failed to download packages'
raise RuntimeError(msg)

# Update the packages count estimate for the next update
Path(packages_count_path).write_text(str(counter), encoding='utf-8')

dispatch(
NotificationsAddAction(
notification=Notification(
@@ -357,7 +359,7 @@ def _(state: _UpdateManagerServiceState) -> None:
dispatch(
NotificationsAddAction(
notification=Notification(
id=UPDATE_MANAGER_NOTIFICATION_ID,
id=UPDATE_MANAGER_SECOND_PHASE_NOTIFICATION_ID,
title='Update in progress',
content="""\
Please keep the device powered on.
@@ -376,4 +378,8 @@ def _(state: _UpdateManagerServiceState) -> None:
),
)
else:
dispatch(NotificationsClearByIdAction(id=UPDATE_MANAGER_NOTIFICATION_ID))
dispatch(
NotificationsClearByIdAction(
id=UPDATE_MANAGER_SECOND_PHASE_NOTIFICATION_ID,
),
)

0 comments on commit 158dc51

Please sign in to comment.